From 6f001ce49223c7e7cc7dc29799f7e82f925d9adc Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Sun, 28 May 2023 21:14:47 +0800 Subject: [PATCH 001/758] fixup! HACK: winex11.drv: Workaround focusing out of exclusive fullscreen windows. XFCE doesn't make fullscreen (without above) windows appear above their panels. However, 21636959 disables NET_WM_STATE_ABOVE for all window managers when NET_WM_STATE_FULLSCREEN is set. CW-Bug-Id: #22269 --- dlls/winex11.drv/window.c | 12 +++++++++++- include/wine/gdi_driver.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index b7cc69532a7..36bc1d58302 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -170,6 +170,8 @@ static int detect_wm(Display *dpy) cached = WINE_WM_X11_STEAMCOMPMGR; else if(strcmp(wm_name, "KWin") == 0) cached = WINE_WM_X11_KDE; + else if(strcmp(wm_name, "Xfwm4") == 0 || strcmp(wm_name, "xfwm4") == 0) + cached = WINE_WM_X11_XFCE4; else cached = WINE_WM_UNKNOWN; @@ -206,6 +208,11 @@ BOOL wm_is_kde(Display *display) return detect_wm(display) == WINE_WM_X11_KDE; } +BOOL wm_is_xfce4(Display *display) +{ + return detect_wm(display) == WINE_WM_X11_XFCE4; +} + BOOL wm_is_steamcompmgr(Display *display) { return detect_wm(display) == WINE_WM_X11_STEAMCOMPMGR; @@ -1280,9 +1287,12 @@ void update_net_wm_states( struct x11drv_win_data *data ) * Many games do not have any specific logic to get out of exclusive fullscreen * mode, and we have currently no way to tell exclusive fullscreen from a window * with topmost + fullscreen styles, so we cannot properly implement it either. + * + * XFCE doesn't make fullscreen (without above) windows appear above their panels. */ - !(new_state & (1 << NET_WM_STATE_FULLSCREEN))) + (wm_is_xfce4(data->display) || !(new_state & (1 << NET_WM_STATE_FULLSCREEN)))) new_state |= (1 << NET_WM_STATE_ABOVE); + if (!data->add_taskbar) { if (data->skip_taskbar || (ex_style & WS_EX_NOACTIVATE) diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 96bedd7acab..f556da495e9 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -347,6 +347,7 @@ extern void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT #define WINE_WM_X11_MUTTER 1 #define WINE_WM_X11_STEAMCOMPMGR 2 #define WINE_WM_X11_KDE 3 +#define WINE_WM_X11_XFCE4 4 static inline LONG_PTR __wine_get_window_manager(void) { From 2cb1b2bf2bddc4c48f8ffa3512a47ad0500d2dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 23:49:31 +0200 Subject: [PATCH 002/758] evr/tests: Test IMFVideoDisplayControl_GetCurrentImage orientation. (cherry picked from commit df91b31428ab22fe26c82729d80ef064b5bc8dfc) CW-Bug-Id: #21316 --- dlls/evr/tests/Makefile.in | 2 + dlls/evr/tests/evr.c | 269 ++++++++++++++++++++++++++++- dlls/evr/tests/nv12frame.bmp | Bin 0 -> 50742 bytes dlls/evr/tests/resource.rc | 33 ++++ dlls/evr/tests/rgb32frame-flip.bmp | Bin 0 -> 36918 bytes dlls/evr/tests/rgb32frame.bmp | Bin 0 -> 36918 bytes 6 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 dlls/evr/tests/nv12frame.bmp create mode 100644 dlls/evr/tests/resource.rc create mode 100644 dlls/evr/tests/rgb32frame-flip.bmp create mode 100644 dlls/evr/tests/rgb32frame.bmp diff --git a/dlls/evr/tests/Makefile.in b/dlls/evr/tests/Makefile.in index c5db2226ebc..6b89635225a 100644 --- a/dlls/evr/tests/Makefile.in +++ b/dlls/evr/tests/Makefile.in @@ -3,3 +3,5 @@ IMPORTS = dxva2 mfplat mfuuid mf strmiids uuid dxguid ole32 oleaut32 evr d3d9 C_SRCS = \ evr.c + +RC_SRCS = resource.rc diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 4f30dd28570..5147896a9b6 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -31,6 +31,95 @@ static const WCHAR sink_id[] = L"EVR Input0"; +static void load_resource(const WCHAR *filename, const BYTE **data, DWORD *length) +{ + HRSRC resource = FindResourceW(NULL, filename, (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW failed, error %lu\n", GetLastError()); + *data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + *length = SizeofResource(GetModuleHandleW(NULL), resource); +} + +static DWORD compare_rgb32(const BYTE *data, DWORD *length, const RECT *rect, const BYTE *expect) +{ + DWORD x, y, size, diff = 0, width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; + + /* skip BMP header from the dump */ + size = *(DWORD *)(expect + 2 + 2 * sizeof(DWORD)); + *length = *length + size; + expect = expect + size; + + for (y = 0; y < height; y++, data += width * 4, expect += width * 4) + { + if (y < rect->top || y >= rect->bottom) continue; + for (x = 0; x < width; x++) + { + if (x < rect->left || x >= rect->right) continue; + diff += abs((int)expect[4 * x + 0] - (int)data[4 * x + 0]); + diff += abs((int)expect[4 * x + 1] - (int)data[4 * x + 1]); + diff += abs((int)expect[4 * x + 2] - (int)data[4 * x + 2]); + } + } + + size = (rect->right - rect->left) * (rect->bottom - rect->top) * 3; + return diff * 100 / 256 / size; +} + +static void dump_rgb32(const BYTE *data, DWORD length, const RECT *rect, HANDLE output) +{ + DWORD width = (rect->right + 0xf) & ~0xf, height = (rect->bottom + 0xf) & ~0xf; + static const char magic[2] = "BM"; + struct + { + DWORD length; + DWORD reserved; + DWORD offset; + BITMAPINFOHEADER biHeader; + } header = + { + .length = length + sizeof(header) + 2, .offset = sizeof(header) + 2, + .biHeader = + { + .biSize = sizeof(BITMAPINFOHEADER), .biWidth = width, .biHeight = height, .biPlanes = 1, + .biBitCount = 32, .biCompression = BI_RGB, .biSizeImage = width * height * 4, + }, + }; + DWORD written; + BOOL ret; + + ret = WriteFile(output, magic, sizeof(magic), &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == sizeof(magic), "written %lu bytes\n", written); + ret = WriteFile(output, &header, sizeof(header), &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == sizeof(header), "written %lu bytes\n", written); + ret = WriteFile(output, data, length, &written, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(written == length, "written %lu bytes\n", written); +} + +#define check_rgb32_data(a, b, c, d) check_rgb32_data_(__LINE__, a, b, c, d) +static DWORD check_rgb32_data_(int line, const WCHAR *filename, const BYTE *data, DWORD length, const RECT *rect) +{ + WCHAR output_path[MAX_PATH]; + const BYTE *expect_data; + HRSRC resource; + HANDLE output; + + GetTempPathW(ARRAY_SIZE(output_path), output_path); + lstrcatW(output_path, filename); + output = CreateFileW(output_path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(output != INVALID_HANDLE_VALUE, "CreateFileW failed, error %lu\n", GetLastError()); + dump_rgb32(data, length, rect, output); + trace("created %s\n", debugstr_w(output_path)); + CloseHandle(output); + + resource = FindResourceW(NULL, filename, (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW failed, error %lu\n", GetLastError()); + expect_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + + return compare_rgb32(data, &length, rect, expect_data); +} + static void set_rect(MFVideoNormalizedRect *rect, float left, float top, float right, float bottom) { rect->left = left; @@ -2913,8 +3002,8 @@ static void test_mixer_zorder(void) IMFTransform_Release(mixer); } -static IDirect3DSurface9 * create_surface(IDirect3DDeviceManager9 *manager, unsigned int width, - unsigned int height) +static IDirect3DSurface9 * create_surface(IDirect3DDeviceManager9 *manager, UINT fourcc, + unsigned int width, unsigned int height) { IDirectXVideoAccelerationService *service; IDirect3DSurface9 *surface = NULL; @@ -2932,7 +3021,7 @@ static IDirect3DSurface9 * create_surface(IDirect3DDeviceManager9 *manager, unsi (void **)&service); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IDirectXVideoAccelerationService_CreateSurface(service, width, height, 0, D3DFMT_A8R8G8B8, + hr = IDirectXVideoAccelerationService_CreateSurface(service, width, height, 0, fourcc, D3DPOOL_DEFAULT, 0, DXVA2_VideoProcessorRenderTarget, &surface, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -3081,7 +3170,7 @@ static void test_mixer_samples(void) IMFSample_Release(sample); - surface = create_surface(manager, 64, 64); + surface = create_surface(manager, D3DFMT_A8R8G8B8, 64, 64); hr = MFCreateVideoSampleFromSurface((IUnknown *)surface, &sample); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -3230,6 +3319,174 @@ static void test_mixer_samples(void) DestroyWindow(window); } +static void test_presenter_orientation(const GUID *subtype) +{ + IMFTopologyServiceLookupClient *lookup_client; + static const BITMAPINFOHEADER expect_header = + { + .biSize = sizeof(BITMAPINFOHEADER), + .biWidth = 96, .biHeight = 96, + .biPlanes = 1, .biBitCount = 32, + .biCompression = BI_RGB, + .biSizeImage = 96 * 96 * 4, + }; + BITMAPINFOHEADER header = {.biSize = sizeof(BITMAPINFOHEADER)}; + IMFVideoDisplayControl *display_control; + DWORD diff, data_size, frame_data_len; + IDirect3DDeviceManager9 *manager; + D3DLOCKED_RECT d3d_rect = {0}; + IMFVideoPresenter *presenter; + IDirect3DSurface9 *surface; + IMFMediaType *video_type; + const BYTE *frame_data; + struct test_host host; + IMFTransform *mixer; + LONGLONG timestamp; + IMFSample *sample; + LONG stride; + HWND window; + BYTE *data; + HRESULT hr; + RECT rect; + + window = create_window(); + + hr = MFCreateVideoMixer(NULL, &IID_IDirect3DDevice9, &IID_IMFTransform, (void **)&mixer); + ok(hr == S_OK, "Failed to create a mixer, hr %#lx.\n", hr); + hr = MFCreateVideoPresenter(NULL, &IID_IDirect3DDevice9, &IID_IMFVideoPresenter, (void **)&presenter); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + init_test_host(&host, mixer, presenter); + hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFTopologyServiceLookupClient, (void **)&lookup_client); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyServiceLookupClient_InitServicePointers(lookup_client, &host.IMFTopologyServiceLookup_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyServiceLookupClient_Release(lookup_client); + + /* Configure device and media types. */ + + hr = MFGetService((IUnknown *)presenter, &MR_VIDEO_ACCELERATION_SERVICE, &IID_IDirect3DDeviceManager9, (void **)&manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTransform_ProcessMessage(mixer, MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)manager); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IDirect3DDeviceManager9_Release(manager); + + video_type = create_video_type(subtype); + hr = IMFMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, (UINT64)expect_header.biWidth << 32 | expect_header.biHeight); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(video_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFTransform_SetInputType(mixer, 0, video_type, 0); + if (broken(IsEqualGUID(subtype, &MFVideoFormat_NV12) && hr == E_FAIL)) + { + win_skip("Skipping unsupported NV12 format\n"); + IMFMediaType_Release(video_type); + goto skip_tests; + } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTransform_SetOutputType(mixer, 0, video_type, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaType_Release(video_type); + + hr = IMFVideoPresenter_ProcessMessage(presenter, MFVP_MESSAGE_INVALIDATEMEDIATYPE, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFVideoPresenter_ProcessMessage(presenter, MFVP_MESSAGE_BEGINSTREAMING, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (IsEqualGUID(subtype, &MFVideoFormat_NV12)) + { + load_resource(L"nv12frame.bmp", &frame_data, &frame_data_len); + /* skip BMP header and RGB data from the dump */ + data_size = *(DWORD *)(frame_data + 2); + frame_data_len = frame_data_len - data_size; + frame_data = frame_data + data_size; + ok(frame_data_len == 13824, "got length %lu\n", frame_data_len); + } + else + { + load_resource(L"rgb32frame.bmp", &frame_data, &frame_data_len); + /* skip BMP header from the dump */ + data_size = *(DWORD *)(frame_data + 2 + 2 * sizeof(DWORD)); + frame_data_len -= data_size; + frame_data += data_size; + ok(frame_data_len == 36864, "got length %lu\n", frame_data_len); + } + + surface = create_surface(manager, subtype->Data1, expect_header.biWidth, expect_header.biHeight); + ok(!!surface, "Failed to create input surface.\n"); + hr = IDirect3DSurface9_LockRect(surface, &d3d_rect, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (IsEqualGUID(subtype, &MFVideoFormat_RGB32)) + memcpy(d3d_rect.pBits, frame_data, frame_data_len); + else if (IsEqualGUID(subtype, &MFVideoFormat_NV12)) + { + hr = MFGetStrideForBitmapInfoHeader(subtype->Data1, expect_header.biWidth, &stride); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCopyImage(d3d_rect.pBits, d3d_rect.Pitch, frame_data, stride, expect_header.biWidth, expect_header.biHeight); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + frame_data += stride * expect_header.biHeight; + d3d_rect.pBits = (BYTE *)d3d_rect.pBits + d3d_rect.Pitch * expect_header.biHeight; + hr = MFCopyImage(d3d_rect.pBits, d3d_rect.Pitch, frame_data, stride, expect_header.biWidth, expect_header.biHeight / 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + hr = IDirect3DSurface9_UnlockRect(surface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateVideoSampleFromSurface((IUnknown *)surface, &sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IDirect3DSurface9_Release(surface); + + hr = IMFTransform_ProcessInput(mixer, 0, sample, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFVideoPresenter_ProcessMessage(presenter, MFVP_MESSAGE_PROCESSINPUTNOTIFY, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFSample_Release(sample); + + hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFVideoDisplayControl, (void **)&display_control); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFVideoDisplayControl_GetCurrentImage(display_control, &header, &data, &data_size, ×tamp); + if (hr == MF_E_INVALIDREQUEST) + { + Sleep(500); + hr = IMFVideoDisplayControl_GetCurrentImage(display_control, &header, &data, &data_size, ×tamp); + } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFVideoDisplayControl_Release(display_control); + + ok(header.biSize == expect_header.biSize, "Unexpected biSize %#lx\n", header.biSize); + ok(header.biWidth == expect_header.biWidth, "Unexpected biWidth %#lx\n", header.biWidth); + ok(header.biHeight == expect_header.biHeight, "Unexpected biHeight %#lx\n", header.biHeight); + ok(header.biPlanes == expect_header.biPlanes, "Unexpected biPlanes %#x\n", header.biPlanes); + ok(header.biBitCount == expect_header.biBitCount, "Unexpected biBitCount %#x\n", header.biBitCount); + ok(header.biCompression == expect_header.biCompression, "Unexpected biCompression %#lx\n", header.biCompression); + ok(header.biSizeImage == expect_header.biSizeImage, "Unexpected biSizeImage %#lx\n", header.biSizeImage); + ok(header.biXPelsPerMeter == expect_header.biXPelsPerMeter, "Unexpected biXPelsPerMeter %#lx\n", header.biXPelsPerMeter); + ok(header.biYPelsPerMeter == expect_header.biYPelsPerMeter, "Unexpected biYPelsPerMeter %#lx\n", header.biYPelsPerMeter); + ok(header.biClrUsed == expect_header.biClrUsed, "Unexpected biClrUsed %#lx\n", header.biClrUsed); + ok(header.biClrImportant == expect_header.biClrImportant, "Unexpected biClrImportant %#lx\n", header.biClrImportant); + + SetRect(&rect, 0, 0, header.biWidth, header.biHeight); + diff = check_rgb32_data(L"rgb32frame-flip.bmp", data, header.biSizeImage, &rect); + todo_wine + ok(diff <= 3, "Unexpected %lu%% diff\n", diff); + CoTaskMemFree(data); + + hr = IMFVideoPresenter_ProcessMessage(presenter, MFVP_MESSAGE_ENDSTREAMING, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + +skip_tests: + hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFTopologyServiceLookupClient, (void **)&lookup_client); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFTopologyServiceLookupClient_ReleaseServicePointers(lookup_client); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFTopologyServiceLookupClient_Release(lookup_client); + + IMFTransform_Release(mixer); + IMFVideoPresenter_Release(presenter); + + DestroyWindow(window); +} + static void test_MFIsFormatYUV(void) { static const DWORD formats[] = @@ -3333,7 +3590,7 @@ static void test_mixer_render(void) IMFMediaType_Release(output_type); IMFMediaType_Release(video_type); - surface = create_surface(manager, 64, 64); + surface = create_surface(manager, D3DFMT_A8R8G8B8, 64, 64); ok(!!surface, "Failed to create input surface.\n"); hr = MFCreateVideoSampleFromSurface((IUnknown *)surface, &sample); @@ -3422,6 +3679,8 @@ START_TEST(evr) test_presenter_video_window(); test_presenter_quality_control(); test_presenter_media_type(); + test_presenter_orientation(&MFVideoFormat_NV12); + test_presenter_orientation(&MFVideoFormat_RGB32); test_presenter_shutdown(); test_mixer_output_rectangle(); test_mixer_zorder(); diff --git a/dlls/evr/tests/nv12frame.bmp b/dlls/evr/tests/nv12frame.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f37bdfc4062ef20d0f017e43147767708c51c09b GIT binary patch literal 50742 zcmeI5v5g`@5JmSgm=IQ2M*f+qAykKQEu3&%9U(Vggsb`@2 z5BqxdGeh`$II2FvAO5O48S3yK|1)^3&Vc{;uey_=4*&5#gU9L&_`gj5kNs-o)H6{1 zhkZT!nIZf=9919T4}aC240ZUA{~0`1XTX2_SKY}_hyVDW!DDp>{9mU3yX#}*)H6{1 zhkZT!nIZf=9919T4}aC240ZUA{~0`1XTX2_SKY}_hyVDW!DDp>{9mU3o9!@i>KUm1 z!@i#V%n<$_j;fFFhrjAhhC2Mm{|p|hGvGh|tL|i|!+-qG;ITRb{x8%2^{^i~^$b-1 zVPDUFW(a=|N7YC8!(VkLLmmF(e+G}$8So$fRd+Jf;XnRo@K~Jz|Ci~%`gfyF`+x6n zgBuRu01n^)4&VR|-~bNb01n^)4&Xp*2cGsdP4I2m?< zEuXu6mHpe}@8ri!;RBzTK>XU)pNoB!{oCU&XU)pS69J{oCW$ zr=RSz(f)5W68H|H{$I2s@Et_`AFfXYzJsX$%dLsP zcM$b2eb)=&z;%HCZX3cs{@u@T=6U+3|IGcmYo~wucN@w)AMJmvPl4|s>VH>rf$t#d z|81=Zd3Zqp8n}S zbHDD|qy4MY%OAjYfd6hg!ax4|ypPd8{nP*Qzt_=cXKfxk`w#n1n>}TFE&9*d|M^S; z-$Ath{d(CR_zt4}-^-f7cM$deTs8&1gQ)*f&wWA*4z8E~?)Ceg;lG{!@ZXNdXpi{E zf3%%dn4^FCUxkjN$3Xun`@j1b34909?_aNr9f9v4>i?)r1-^r*|En?)_zt4}Rq6?7 zupQvP+m7&$|32?y^iTivzx@AT`s}RDV`u-#x&Ms6>TlnG?I7C!-eNBB9Yp=V)r%j% zcM$deRQ-eRAnLzq8iDTs|Gq<~L;U04=_mUv{nLN;hMo1(Km9xXWS^yf`p@35v;JuR EKX0Xo-v9sr literal 0 HcmV?d00001 diff --git a/dlls/evr/tests/rgb32frame.bmp b/dlls/evr/tests/rgb32frame.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9f2ea1e5d1b2b808f3671b09c19ed6fcd090de85 GIT binary patch literal 36918 zcmeI*u}Yg^9EI^Cp-Y{_DXoJ;gGccke(i!PP-=^b)K~Zl@ik*$ag_DEVIB zs5js_AC!Oc1db^3!j~V9MuH??za3ub`F7Ype(pPd-In(IXZ`m2{g*FZJ`TemmK(IQ zCTG~^_A5ijesu(H@#ALzek|36w2%MINA@47$B z4P5_^j}KzGf$Kl}_g5@8aQ)SmVz~kSxna^F{`e>TR6k4q^k2Q<*nvxc=YoC6*hw{_2lG zEH}VEH%vOjAOEDE>SyVn{;M~fTtEHOf6`C&v-D5@)f-N(-|c^?K`b}GKQ~M|#2^2p zpXz7npZ=>ioLoQs(|^)W^|SO(|J55#uHWtd=6w^(4P5`bNq_Ad78|(!AM_xW8@T>6 z_1C^(v4QKa{_H2q4e-wmlMeC6Kk29XS^B5{>J2B?Pyh6v^i%z;+yBYVTr4+m{Xe~1 zh~);Z|4&WDas$`@LPuh`f$KkPNh~+OKQ~M|#2^2ppXz7npZ=>ioLoQs(|^)W^|NmO z3w;sG4P5_w?TY0Fu75l4?_17tgEQ~_-`jOR$8rPLpL1`2%|gsDqX8PA0UDqI8lV9h MpaB}7fpQHz0TpqG-v9sr literal 0 HcmV?d00001 From 4cd71cb9f2622b45cfa077335d6afffa68b9ac54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 11:13:49 +0200 Subject: [PATCH 003/758] evr: Respect RGB format stride in GetCurrentImage. (cherry picked from commit 486531ca8a8d0dc3aff2804c574928a37c96664c) CW-Bug-Id: #21316 --- dlls/evr/presenter.c | 3 ++- dlls/evr/tests/evr.c | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index d8bdbee3915..06592f8766e 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -1531,7 +1531,8 @@ static HRESULT WINAPI video_presenter_control_GetCurrentImage(IMFVideoDisplayCon { if (SUCCEEDED(hr = IDirect3DSurface9_LockRect(readback, &mapped_rect, NULL, D3DLOCK_READONLY))) { - memcpy(*dib, mapped_rect.pBits, *dib_size); + hr = MFCopyImage(stride < 0 ? *dib + *dib_size + stride : *dib, stride, + mapped_rect.pBits, mapped_rect.Pitch, abs(stride), surface_desc.Height); IDirect3DSurface9_UnlockRect(readback); } } diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 5147896a9b6..c01257e0d34 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -3467,8 +3467,7 @@ static void test_presenter_orientation(const GUID *subtype) SetRect(&rect, 0, 0, header.biWidth, header.biHeight); diff = check_rgb32_data(L"rgb32frame-flip.bmp", data, header.biSizeImage, &rect); - todo_wine - ok(diff <= 3, "Unexpected %lu%% diff\n", diff); + ok(diff <= 5, "Unexpected %lu%% diff\n", diff); CoTaskMemFree(data); hr = IMFVideoPresenter_ProcessMessage(presenter, MFVP_MESSAGE_ENDSTREAMING, 0); From 2ab9f8646a5a3c723e459d467f2c386b8d2fd15f Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 9 Jun 2023 15:12:31 +0800 Subject: [PATCH 004/758] fsync: Type-check HANDLE in fsync_reset_event(). Oddworld: Stranger's Wrath HD (15750) calls ResetEvent() on a file handle and then waits for the handle with an infinite timeout. Without esync/fsync, NtResetEvent() should return STATUS_OBJECT_TYPE_MISMATCH because the handle is not an event handle. With esync/fsync, the file handle is set to non-signaled successfully and causes the game to hang at start-up. The same check should also apply to fsync_pulse_event(), esync_reset_event(), and esync_pulse_event(). CW-Bug-Id: #22326 --- dlls/ntdll/unix/fsync.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c index b4d4b086919..aab9096cf14 100644 --- a/dlls/ntdll/unix/fsync.c +++ b/dlls/ntdll/unix/fsync.c @@ -714,6 +714,12 @@ NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) if ((ret = get_object( handle, &obj ))) return ret; event = obj.shm; + if (obj.type != FSYNC_MANUAL_EVENT && obj.type != FSYNC_AUTO_EVENT) + { + put_object( &obj ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); if (prev) *prev = current; From 1ca0faa0d14090ff8bd5b95a6ddbcffb2f279c2e Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 9 Jun 2023 15:13:12 +0800 Subject: [PATCH 005/758] fsync: Type-check HANDLE in fsync_pulse_event(). CW-Bug-Id: #22326 --- dlls/ntdll/unix/fsync.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c index aab9096cf14..d6025a173a8 100644 --- a/dlls/ntdll/unix/fsync.c +++ b/dlls/ntdll/unix/fsync.c @@ -740,6 +740,12 @@ NTSTATUS fsync_pulse_event( HANDLE handle, LONG *prev ) if ((ret = get_object( handle, &obj ))) return ret; event = obj.shm; + if (obj.type != FSYNC_MANUAL_EVENT && obj.type != FSYNC_AUTO_EVENT) + { + put_object( &obj ); + return STATUS_OBJECT_TYPE_MISMATCH; + } + /* This isn't really correct; an application could miss the write. * Unfortunately we can't really do much better. Fortunately this is rarely * used (and publicly deprecated). */ From 1e95c24a9876120100d4a6d1c82a16b59f84d2bf Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 9 Jun 2023 15:14:59 +0800 Subject: [PATCH 006/758] esync: Type-check HANDLE in esync_reset_event(). CW-Bug-Id: #22326 --- dlls/ntdll/unix/esync.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c index 40cf4a07056..948602ef35a 100644 --- a/dlls/ntdll/unix/esync.c +++ b/dlls/ntdll/unix/esync.c @@ -572,6 +572,9 @@ NTSTATUS esync_reset_event( HANDLE handle ) if ((ret = get_object( handle, &obj ))) return ret; event = obj->shm; + if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) + return STATUS_OBJECT_TYPE_MISMATCH; + if (obj->type == ESYNC_MANUAL_EVENT) { /* Acquire the spinlock. */ From 17133385f61e03826aaa84bcbc54cf339d4132d5 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 9 Jun 2023 15:15:55 +0800 Subject: [PATCH 007/758] esync: Type-check HANDLE in esync_pulse_event(). CW-Bug-Id: #22326 --- dlls/ntdll/unix/esync.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c index 948602ef35a..56fdd150175 100644 --- a/dlls/ntdll/unix/esync.c +++ b/dlls/ntdll/unix/esync.c @@ -615,6 +615,9 @@ NTSTATUS esync_pulse_event( HANDLE handle ) if ((ret = get_object( handle, &obj ))) return ret; + if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) + return STATUS_OBJECT_TYPE_MISMATCH; + /* This isn't really correct; an application could miss the write. * Unfortunately we can't really do much better. Fortunately this is rarely * used (and publicly deprecated). */ From d0cb048bfdedc77164a1a4451b6ceeb8ba1a493a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 29 Jun 2023 11:48:55 +0200 Subject: [PATCH 008/758] winegstreamer: Allow concurrent wait_on_sample in the media source. Fixes deadlock in Disaster Report 4: Summer Memories. CW-Bug-Id: #22377 --- dlls/winegstreamer/media_source.c | 37 +++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e6e78fd4de3..6c00f103662 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -44,6 +44,8 @@ struct media_stream LONG token_queue_count; LONG token_queue_cap; + CRITICAL_SECTION cs; + DWORD stream_id; BOOL active; BOOL eos; @@ -532,19 +534,34 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) struct media_source *source = impl_from_IMFMediaSource(stream->media_source); PROPVARIANT empty_var = {.vt = VT_EMPTY}; struct wg_parser_buffer buffer; + BOOL ret; TRACE("%p, %p\n", stream, token); - if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) + EnterCriticalSection(&stream->cs); + + if (!stream->wg_stream) { - send_buffer(stream, &buffer, token); + LeaveCriticalSection(&stream->cs); + return; } + + LeaveCriticalSection(&source->cs); + ret = wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer); + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + WARN("media source has been shutdown, returning\n"); + else if (ret) + send_buffer(stream, &buffer, token); else { stream->eos = TRUE; IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); dispatch_end_of_presentation(source); } + + LeaveCriticalSection(&stream->cs); } static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -704,6 +721,8 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) IMFStreamDescriptor_Release(stream->descriptor); IMFMediaEventQueue_Release(stream->event_queue); flush_token_queue(stream, FALSE); + stream->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&stream->cs); free(stream); } @@ -868,6 +887,9 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, object->eos = FALSE; object->wg_stream = wg_parser_get_stream(wg_parser, id); + InitializeCriticalSection(&object->cs); + object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + TRACE("Created stream object %p.\n", object); *out = object; @@ -1387,6 +1409,7 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *source = impl_from_IMFMediaSource(iface); + UINT i; TRACE("%p.\n", iface); @@ -1400,6 +1423,16 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) source->state = SOURCE_SHUTDOWN; + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + wg_parser_stream_disable(stream->wg_stream); + + EnterCriticalSection(&stream->cs); + stream->wg_stream = NULL; + LeaveCriticalSection(&stream->cs); + } + wg_parser_disconnect(source->wg_parser); source->read_thread_shutdown = true; From 0bbe8ac9e4d21c37ad8081f02ba7737e92e7b132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 29 Jun 2023 14:36:32 +0200 Subject: [PATCH 009/758] winhttp: Move connect end checks out of the loop. This is only done when InitializeSecurityContextW returns SEC_E_OK, which will break out of the loop, and the checks forcefully break out of the loop if any failed. CW-Bug-Id: #18449 CW-Bug-Id: #22371 --- dlls/winhttp/net.c | 80 ++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index 1be78d126d4..2e655bed5a5 100644 --- a/dlls/winhttp/net.c +++ b/dlls/winhttp/net.c @@ -384,60 +384,58 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc, 0, NULL, &out_desc, &attrs, NULL); TRACE( "InitializeSecurityContext ret %#lx\n", status ); + if(status == SEC_E_OK && in_bufs[1].BufferType == SECBUFFER_EXTRA) + FIXME("SECBUFFER_EXTRA not supported\n"); + } - if(status == SEC_E_OK) { - if(in_bufs[1].BufferType == SECBUFFER_EXTRA) - FIXME("SECBUFFER_EXTRA not supported\n"); - - status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes); - if(status != SEC_E_OK) { - WARN("Could not get sizes\n"); - break; - } + free(read_buf); - status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert); - if(status == SEC_E_OK) { - res = netconn_verify_cert(cert, hostname, security_flags, check_revocation); - CertFreeCertificateContext(cert); - if(res != ERROR_SUCCESS) { - WARN( "cert verify failed: %lu\n", res ); - break; - } - }else { - WARN("Could not get cert\n"); - break; - } + if(status != SEC_E_OK || res != ERROR_SUCCESS) + goto failed; - conn->ssl_read_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer); - if(!conn->ssl_read_buf) { - res = ERROR_OUTOFMEMORY; - break; - } - conn->ssl_write_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer); - if(!conn->ssl_write_buf) { - res = ERROR_OUTOFMEMORY; - break; - } - } + status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes); + if(status != SEC_E_OK) { + WARN("Could not get sizes\n"); + goto failed; } - free(read_buf); + status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert); + if(status != SEC_E_OK) { + WARN("Could not get cert\n"); + goto failed; + } - if(status != SEC_E_OK || res != ERROR_SUCCESS) { - WARN( "Failed to initialize security context: %#lx\n", status ); - free(conn->ssl_read_buf); - conn->ssl_read_buf = NULL; - free(conn->ssl_write_buf); - conn->ssl_write_buf = NULL; - DeleteSecurityContext(&ctx); - return ERROR_WINHTTP_SECURE_CHANNEL_ERROR; + res = netconn_verify_cert(cert, hostname, security_flags, check_revocation); + CertFreeCertificateContext(cert); + if(res != ERROR_SUCCESS) { + WARN( "cert verify failed: %lu\n", res ); + goto failed; } + conn->ssl_read_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer); + if(!conn->ssl_read_buf) { + res = ERROR_OUTOFMEMORY; + goto failed; + } + conn->ssl_write_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer); + if(!conn->ssl_write_buf) { + res = ERROR_OUTOFMEMORY; + goto failed; + } TRACE("established SSL connection\n"); conn->secure = TRUE; conn->ssl_ctx = ctx; return ERROR_SUCCESS; + +failed: + WARN( "Failed to initialize security context: %#lx\n", status ); + free(conn->ssl_read_buf); + conn->ssl_read_buf = NULL; + free(conn->ssl_write_buf); + conn->ssl_write_buf = NULL; + DeleteSecurityContext(&ctx); + return ERROR_WINHTTP_SECURE_CHANNEL_ERROR; } static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size, WSAOVERLAPPED *ovr ) From f026379bbd7464d04ce6750abba88222783c024c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 29 Jun 2023 14:39:06 +0200 Subject: [PATCH 010/758] winhttp: Introduce new netconn_negotiate helper. CW-Bug-Id: #18449 CW-Bug-Id: #22371 --- dlls/winhttp/net.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index 2e655bed5a5..4eb7b601d0e 100644 --- a/dlls/winhttp/net.c +++ b/dlls/winhttp/net.c @@ -303,28 +303,24 @@ void netconn_release( struct netconn *conn ) free(conn); } -DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle, - BOOL check_revocation ) +static DWORD netconn_negotiate( struct netconn *conn, WCHAR *hostname, CredHandle *cred_handle, + CtxtHandle *ctx ) { SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}}; SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs}; BYTE *read_buf; SIZE_T read_buf_size = 2048; ULONG attrs = 0; - CtxtHandle ctx; SSIZE_T size; - const CERT_CONTEXT *cert; SECURITY_STATUS status; - DWORD res = ERROR_SUCCESS; const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION; if (!(read_buf = malloc( read_buf_size ))) return ERROR_OUTOFMEMORY; - memset( &ctx, 0, sizeof(ctx) ); status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0, - &ctx, &out_desc, &attrs, NULL); + ctx, &out_desc, &attrs, NULL); assert(status != SEC_E_OK); @@ -337,7 +333,7 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, NULL); if(size != out_buf.cbBuffer) { ERR("send failed\n"); - res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; + status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; break; } @@ -381,7 +377,7 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur in_bufs[0].cbBuffer += size; in_bufs[0].pvBuffer = read_buf; - status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc, + status = InitializeSecurityContextW(cred_handle, ctx, hostname, isc_req_flags, 0, 0, &in_desc, 0, NULL, &out_desc, &attrs, NULL); TRACE( "InitializeSecurityContext ret %#lx\n", status ); if(status == SEC_E_OK && in_bufs[1].BufferType == SECBUFFER_EXTRA) @@ -390,6 +386,18 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur free(read_buf); + return status; +} + +DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle, + BOOL check_revocation ) +{ + CtxtHandle ctx = {0}; + const CERT_CONTEXT *cert; + SECURITY_STATUS status; + DWORD res = ERROR_SUCCESS; + + status = netconn_negotiate(conn, hostname, cred_handle, &ctx); if(status != SEC_E_OK || res != ERROR_SUCCESS) goto failed; From 195122c5d6d1cecdeda1a3c8dd0a7cce0e51e22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 29 Jun 2023 16:01:39 +0200 Subject: [PATCH 011/758] winhttp: Handle SEC_I_RENEGOTIATE after DecryptMessage. By performing renegotiation as we should, instead of incorrectly returning ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. MSDN says we should pass returned SECBUFFER_EXTRA as SECBUFFER_TOKEN, so we also do that, although it's usually empty. CW-Bug-Id: #18449 CW-Bug-Id: #22371 --- dlls/winhttp/net.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index 4eb7b601d0e..c10438df624 100644 --- a/dlls/winhttp/net.c +++ b/dlls/winhttp/net.c @@ -304,7 +304,7 @@ void netconn_release( struct netconn *conn ) } static DWORD netconn_negotiate( struct netconn *conn, WCHAR *hostname, CredHandle *cred_handle, - CtxtHandle *ctx ) + CtxtHandle *prev_ctx, SecBufferDesc *prev_buf, CtxtHandle *ctx ) { SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}}; SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs}; @@ -319,8 +319,9 @@ static DWORD netconn_negotiate( struct netconn *conn, WCHAR *hostname, CredHandl if (!(read_buf = malloc( read_buf_size ))) return ERROR_OUTOFMEMORY; - status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0, + status = InitializeSecurityContextW(cred_handle, prev_ctx, hostname, isc_req_flags, 0, 0, prev_buf, 0, ctx, &out_desc, &attrs, NULL); + if (!ctx) ctx = prev_ctx; assert(status != SEC_E_OK); @@ -397,7 +398,7 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur SECURITY_STATUS status; DWORD res = ERROR_SUCCESS; - status = netconn_negotiate(conn, hostname, cred_handle, &ctx); + status = netconn_negotiate(conn, hostname, cred_handle, NULL, NULL, &ctx); if(status != SEC_E_OK || res != ERROR_SUCCESS) goto failed; @@ -558,8 +559,23 @@ static DWORD read_ssl_chunk( struct netconn *conn, void *buf, SIZE_T buf_size, S break; case SEC_I_RENEGOTIATE: + { + SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}; + TRACE("renegotiate\n"); - return ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED; + + for(i = 0; i < ARRAY_SIZE(bufs); i++) { + if(bufs[i].BufferType == SECBUFFER_EXTRA) { + out_buf.cbBuffer = bufs[i].cbBuffer; + out_buf.pvBuffer = bufs[i].pvBuffer; + } + } + + res = netconn_negotiate(conn, conn->host->hostname, NULL, &conn->ssl_ctx, &out_desc, NULL); + if (res != SEC_E_OK) return res; + continue; + } case SEC_I_CONTEXT_EXPIRED: TRACE("context expired\n"); From 13c373f86cb5af3db6fa5e6881fc5e4fe3a369d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 3 Dec 2020 11:26:56 +0100 Subject: [PATCH 012/758] secur32: Perform TLS re-handshake after SEC_I_RENEGOTIATE was returned. Even if input buffer is empty, as this is often the case. CW-Bug-Id: #18449 CW-Bug-Id: #22371 --- dlls/secur32/schannel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 6b5df3520a7..d4aab2b4c2a 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -64,6 +64,7 @@ struct schan_context const CERT_CONTEXT *cert; SIZE_T header_size; BOOL shutdown_requested; + BOOL rehandshake_requested; }; static struct schan_handle *schan_handle_table; @@ -875,7 +876,7 @@ static SECURITY_STATUS establish_context( buffer = &pInput->pBuffers[idx]; ptr = buffer->pvBuffer; - if (buffer->cbBuffer < ctx->header_size) + if (buffer->cbBuffer < ctx->header_size && !ctx->rehandshake_requested) { TRACE("Expected at least %Iu bytes, but buffer only contains %lu bytes.\n", ctx->header_size, buffer->cbBuffer); @@ -892,7 +893,7 @@ static SECURITY_STATUS establish_context( ptr += record_size; } - if (!expected_size) + if (!expected_size && !ctx->rehandshake_requested) { TRACE("Expected at least %Iu bytes, but buffer only contains %lu bytes.\n", max(ctx->header_size, record_size), buffer->cbBuffer); @@ -942,6 +943,7 @@ static SECURITY_STATUS establish_context( params.output_offset = &output_offset; params.control_token = ctx->shutdown_requested ? control_token_shutdown : control_token_none; ctx->shutdown_requested = FALSE; + ctx->rehandshake_requested = FALSE; ret = GNUTLS_CALL( handshake, ¶ms ); if (output_buffer_idx != -1) @@ -1563,6 +1565,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle buffer->BufferType = SECBUFFER_STREAM_HEADER; buffer->cbBuffer = ctx->header_size; + if (status == SEC_I_RENEGOTIATE) ctx->rehandshake_requested = TRUE; return status; } From 20b191b0197b50660a53c22d0a50c0e04a8078e3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 3 Jul 2023 13:30:33 -0600 Subject: [PATCH 013/758] fsync: Check for NULL handle in get_object(). CW-Bug-Id: #22395 --- dlls/ntdll/unix/fsync.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c index d6025a173a8..c265d6c02e0 100644 --- a/dlls/ntdll/unix/fsync.c +++ b/dlls/ntdll/unix/fsync.c @@ -402,6 +402,7 @@ static NTSTATUS get_object( HANDLE handle, struct fsync *obj ) return STATUS_NOT_IMPLEMENTED; } + if (!handle) return STATUS_INVALID_HANDLE; /* We need to try grabbing it from the server. Uninterrupted section * is needed to avoid race with NtClose() which first calls fsync_close() From 261e80cc7cfd6d817ce526926a726e415721e5a3 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 27 Jun 2023 23:01:34 +0300 Subject: [PATCH 014/758] win32u: Don't affect nonclient area unless requested. The state of the caption / nonclient area should be only changed when FLASHW_CAPTION or FLASHW_STOP are used. (cherry picked from commit d4b2865eb7f2612e52b3fee6ae121e59c2d28296) CW-Bug-Id: #22348 --- dlls/win32u/window.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 726ac15fc34..a07be55e883 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4528,11 +4528,11 @@ BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info ) win = get_win_ptr( info->hwnd ); if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE; - if (info->dwFlags && !(win->flags & WIN_NCACTIVATED)) + if (info->dwFlags & FLASHW_CAPTION && !(win->flags & WIN_NCACTIVATED)) { win->flags |= WIN_NCACTIVATED; } - else + else if (!info->dwFlags) { win->flags &= ~WIN_NCACTIVATED; } @@ -4553,7 +4553,10 @@ BOOL WINAPI NtUserFlashWindowEx( FLASHWINFO *info ) else wparam = (hwnd == NtUserGetForegroundWindow()); release_win_ptr( win ); - send_notify_message( hwnd, WM_NCACTIVATE, wparam, 0, 0 ); + + if (!info->dwFlags || info->dwFlags & FLASHW_CAPTION) + send_notify_message( hwnd, WM_NCACTIVATE, wparam, 0, 0 ); + user_driver->pFlashWindowEx( info ); return wparam; } From f4e0808122160eaeeaf8a7b8a669b0c931bcf5c0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 14 Jul 2023 19:15:52 -0600 Subject: [PATCH 015/758] ntdll/tests: Add test for async cancel on pipe's last process handle close. (cherry picked from commit f311bb5fba84c7574ab32c82aee345cccf310223) CW-Bug-Id: #22436 --- dlls/ntdll/tests/pipe.c | 143 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index 11192d6e471..70aa1ebda95 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -131,6 +131,24 @@ static BOOL init_func_ptrs(void) return TRUE; } +static HANDLE create_process(const char *arg) +{ + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi; + char cmdline[MAX_PATH]; + char **argv; + BOOL ret; + + si.cb = sizeof(si); + winetest_get_mainargs(&argv); + sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "got %lu.\n", GetLastError()); + ret = CloseHandle(pi.hThread); + ok(ret, "got %lu.\n", GetLastError()); + return pi.hProcess; +} + static inline BOOL is_signaled( HANDLE obj ) { return WaitForSingleObject( obj, 0 ) == WAIT_OBJECT_0; @@ -2971,13 +2989,137 @@ static void test_pipe_names(void) } } +static void test_async_cancel_on_handle_close(void) +{ + static const struct + { + BOOL event; + PIO_APC_ROUTINE apc; + BOOL apc_context; + } + tests[] = + { + {TRUE, NULL}, + {FALSE, NULL}, + {TRUE, ioapc}, + {FALSE, ioapc}, + {TRUE, NULL, TRUE}, + {FALSE, NULL, TRUE}, + {TRUE, ioapc, TRUE}, + {FALSE, ioapc, TRUE}, + }; + + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + char read_buf[16]; + HANDLE port, write, read, event, handle2, process_handle; + IO_STATUS_BLOCK io; + NTSTATUS status; + unsigned int i, other_process; + DWORD ret; + BOOL bret; + + create_pipe_pair(&read, &write, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096); + + status = pNtQueryInformationFile(read, &io, &info, sizeof(info), + FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS || broken(status == STATUS_INVALID_INFO_CLASS), + "status = %lx\n", status); + CloseHandle(read); + CloseHandle(write); + if (status) + { + win_skip("FileIoCompletionNotificationInformation is not supported.\n"); + return; + } + + process_handle = create_process("sleep"); + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + for (other_process = 0; other_process < 2; ++other_process) + { + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("other_process %u, i %u", other_process, i); + create_pipe_pair(&read, &write, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096); + port = CreateIoCompletionPort(read, NULL, 0, 0); + ok(!!port, "got %p.\n", port); + + memset(&io, 0xcc, sizeof(io)); + ResetEvent(event); + status = NtReadFile(read, tests[i].event ? event : NULL, tests[i].apc, tests[i].apc_context ? &io : NULL, &io, + read_buf, 16, NULL, NULL); + if (tests[i].apc) + { + ok(status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status); + CloseHandle(read); + CloseHandle(write); + CloseHandle(port); + winetest_pop_context(); + continue; + } + ok(status == STATUS_PENDING, "got %#lx.\n", status); + ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + + bret = DuplicateHandle(GetCurrentProcess(), read, other_process ? process_handle : GetCurrentProcess(), + &handle2, 0, FALSE, DUPLICATE_SAME_ACCESS); + ok(bret, "failed, error %lu.\n", GetLastError()); + + CloseHandle(read); + ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + if (other_process && tests[i].apc_context && !tests[i].event) + todo_wine test_queued_completion(port, &io, STATUS_CANCELLED, 0); + else + test_no_queued_completion(port); + + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_TIMEOUT, "got %#lx.\n", ret); + + if (other_process) + { + bret = DuplicateHandle(process_handle, handle2, GetCurrentProcess(), &read, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + ok(bret, "failed, error %lu.\n", GetLastError()); + } + else + { + read = handle2; + } + CloseHandle(read); + CloseHandle(write); + CloseHandle(port); + winetest_pop_context(); + } + } + + CloseHandle(event); + TerminateProcess(process_handle, 0); + WaitForSingleObject(process_handle, INFINITE); + CloseHandle(process_handle); +} + START_TEST(pipe) { + char **argv; + int argc; + if (!init_func_ptrs()) return; if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + argc = winetest_get_mainargs(&argv); + if (argc >= 3) + { + if (!strcmp(argv[2], "sleep")) + { + Sleep(5000); + return; + } + return; + } + trace("starting invalid create tests\n"); test_create_invalid(); @@ -3031,6 +3173,7 @@ START_TEST(pipe) test_security_info(); test_empty_name(); test_pipe_names(); + test_async_cancel_on_handle_close(); pipe_for_each_state(create_pipe_server, connect_pipe, test_pipe_state); pipe_for_each_state(create_pipe_server, connect_and_write_pipe, test_pipe_with_data_state); From 555cb98a15d205bf09df4b44b3e6369439abf341 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 14 Jul 2023 19:57:51 -0600 Subject: [PATCH 016/758] ws2_32/tests: Add test for async cancel on socket's last process handle close. (cherry picked from commit 6360f56931bad30dd6381d317570ccee98393e66) CW-Bug-Id: #22436 --- dlls/ws2_32/tests/afd.c | 184 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 172 insertions(+), 12 deletions(-) diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index b07fb40fe3f..73d6769ff8b 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -33,6 +33,24 @@ #define TIMEOUT_INFINITE _I64_MAX +static HANDLE create_process(const char *arg) +{ + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi; + char cmdline[MAX_PATH]; + char **argv; + BOOL ret; + + si.cb = sizeof(si); + winetest_get_mainargs(&argv); + sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "got %lu.\n", GetLastError()); + ret = CloseHandle(pi.hThread); + ok(ret, "got %lu.\n", GetLastError()); + return pi.hProcess; +} + static void tcp_socketpair_flags(SOCKET *src, SOCKET *dst, DWORD flags) { SOCKET server = INVALID_SOCKET; @@ -2457,11 +2475,11 @@ static NTSTATUS WINAPI thread_NtDeviceIoControlFile(BOOL kill_thread, HANDLE han return p.ret; } -static unsigned int test_async_thread_termination_apc_count; +static unsigned int test_apc_count; -static void WINAPI test_async_thread_termination_apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved ) +static void WINAPI test_apc_proc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved ) { - ++test_async_thread_termination_apc_count; + ++test_apc_count; } static void test_async_thread_termination(void) @@ -2479,18 +2497,18 @@ static void test_async_thread_termination(void) {TRUE, TRUE, NULL, NULL}, {FALSE, FALSE, NULL, NULL}, {TRUE, FALSE, NULL, NULL}, - {FALSE, TRUE, test_async_thread_termination_apc, NULL}, - {TRUE, TRUE, test_async_thread_termination_apc, NULL}, - {FALSE, FALSE, test_async_thread_termination_apc, NULL}, - {TRUE, FALSE, test_async_thread_termination_apc, NULL}, + {FALSE, TRUE, test_apc_proc, NULL}, + {TRUE, TRUE, test_apc_proc, NULL}, + {FALSE, FALSE, test_apc_proc, NULL}, + {TRUE, FALSE, test_apc_proc, NULL}, {FALSE, TRUE, NULL, (void *)0xdeadbeef}, {TRUE, TRUE, NULL, (void *)0xdeadbeef}, {FALSE, FALSE, NULL, (void *)0xdeadbeef}, {TRUE, FALSE, NULL, (void *)0xdeadbeef}, - {FALSE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef}, - {TRUE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef}, - {FALSE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef}, - {TRUE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef}, + {FALSE, TRUE, test_apc_proc, (void *)0xdeadbeef}, + {TRUE, TRUE, test_apc_proc, (void *)0xdeadbeef}, + {FALSE, FALSE, test_apc_proc, (void *)0xdeadbeef}, + {TRUE, FALSE, test_apc_proc, (void *)0xdeadbeef}, }; const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; @@ -2543,7 +2561,7 @@ static void test_async_thread_termination(void) } SleepEx(0, TRUE); - ok(!test_async_thread_termination_apc_count, "got APC.\n"); + ok(!test_apc_count, "got APC.\n"); port = CreateIoCompletionPort((HANDLE)listener, NULL, 0, 0); @@ -2747,12 +2765,153 @@ static void test_read_write(void) CloseHandle(event); } +static void test_async_cancel_on_handle_close(void) +{ + static const struct + { + BOOL event; + PIO_APC_ROUTINE apc; + void *apc_context; + } + tests[] = + { + {TRUE, NULL, NULL}, + {FALSE, NULL, NULL}, + {TRUE, test_apc_proc, NULL}, + {FALSE, test_apc_proc, NULL}, + {TRUE, NULL, (void *)0xdeadbeef}, + {FALSE, NULL, (void *)0xdeadbeef}, + {TRUE, test_apc_proc, (void *)0xdeadbeef}, + {FALSE, test_apc_proc, (void *)0xdeadbeef}, + }; + + const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; + char in_buffer[offsetof(struct afd_poll_params, sockets[3])]; + char out_buffer[offsetof(struct afd_poll_params, sockets[3])]; + struct afd_poll_params *in_params = (struct afd_poll_params *)in_buffer; + struct afd_poll_params *out_params = (struct afd_poll_params *)out_buffer; + unsigned int i, other_process; + LARGE_INTEGER zero = {{0}}; + HANDLE process_handle; + ULONG_PTR key, value; + IO_STATUS_BLOCK io; + HANDLE event, port; + ULONG params_size; + SOCKET listener; + HANDLE handle2; + DWORD ret; + BOOL bret; + + process_handle = create_process("sleep"); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + in_params->count = 1; + in_params->exclusive = FALSE; + in_params->sockets[0].flags = ~0; + in_params->sockets[0].status = 0xdeadbeef; + params_size = offsetof(struct afd_poll_params, sockets[1]); + in_params->timeout = -10 * 1000 * 1000 * 5; + + for (other_process = 0; other_process < 2; ++other_process) + { + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("other_process %u, i %u", other_process, i); + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ret = bind(listener, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + ret = listen(listener, 1); + ok(!ret, "got error %u\n", WSAGetLastError()); + + port = CreateIoCompletionPort((HANDLE)listener, NULL, 0, 0); + ok(!!port, "got %p.\n", port); + + in_params->sockets[0].socket = listener; + + memset(&io, 0xcc, sizeof(io)); + ResetEvent(event); + ret = NtDeviceIoControlFile((HANDLE)listener, tests[i].event ? event : NULL, + tests[i].apc, tests[i].apc_context, &io, IOCTL_AFD_POLL, in_params, params_size, + out_params, params_size); + if (tests[i].apc) + { + ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + winetest_pop_context(); + continue; + } + ok(ret == STATUS_PENDING, "got %#lx.\n", ret); + ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + + bret = DuplicateHandle(GetCurrentProcess(), (HANDLE)listener, + other_process ? process_handle : GetCurrentProcess(), + &handle2, 0, FALSE, DUPLICATE_SAME_ACCESS); + ok(bret, "failed, error %lu.\n", GetLastError()); + + closesocket(listener); + + ok(io.Status == 0xcccccccc, "got %#lx\n", io.Status); + memset(&io, 0xcc, sizeof(io)); + key = 0xcc; + value = 0; + ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); + if (other_process && tests[i].apc_context && !tests[i].event) + { + todo_wine ok(!ret, "got %#lx\n", ret); + todo_wine ok(!key, "got key %#Ix\n", key); + todo_wine ok(value == 0xdeadbeef, "got value %#Ix\n", value); + todo_wine ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status); + } + else + { + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + } + + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_TIMEOUT, "got %#lx.\n", ret); + + if (other_process) + { + bret = DuplicateHandle(process_handle, handle2, GetCurrentProcess(), (HANDLE *)&listener, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + ok(bret, "failed, error %lu.\n", GetLastError()); + } + else + { + listener = (SOCKET)handle2; + } + + CloseHandle((HANDLE)listener); + CloseHandle(port); + winetest_pop_context(); + } + } + CloseHandle(event); + TerminateProcess(process_handle, 0); + WaitForSingleObject(process_handle, INFINITE); + CloseHandle(process_handle); +} + START_TEST(afd) { WSADATA data; + char **argv; + int argc; WSAStartup(MAKEWORD(2, 2), &data); + argc = winetest_get_mainargs(&argv); + if (argc >= 3) + { + if (!strcmp(argv[2], "sleep")) + { + Sleep(5000); + return; + } + return; + } + test_open_device(); test_poll(); test_poll_exclusive(); @@ -2766,6 +2925,7 @@ START_TEST(afd) test_getsockname(); test_async_thread_termination(); test_read_write(); + test_async_cancel_on_handle_close(); WSACleanup(); } From 738664f0cd7d9368eb30b51af8a2a58d38687939 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 19 Jul 2023 10:02:06 -0600 Subject: [PATCH 017/758] server: Cancel socket asyncs when the last handle in process is closed. (cherry picked from commit 57095a91b053a5570bd797782b51b84fdd42173c) CW-Bug-Id: #22436 --- dlls/ws2_32/tests/afd.c | 11 +++++++---- server/async.c | 22 ++++++++++++++++++++++ server/file.h | 1 + server/handle.c | 15 +++++++++++++++ server/handle.h | 1 + server/sock.c | 3 +-- 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index 73d6769ff8b..4fabf478f03 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -2851,17 +2851,20 @@ static void test_async_cancel_on_handle_close(void) closesocket(listener); + /* Canceled asyncs with completion port and no event do not update IOSB before removing completion. */ + todo_wine_if(other_process && tests[i].apc_context && !tests[i].event) ok(io.Status == 0xcccccccc, "got %#lx\n", io.Status); + memset(&io, 0xcc, sizeof(io)); key = 0xcc; value = 0; ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); if (other_process && tests[i].apc_context && !tests[i].event) { - todo_wine ok(!ret, "got %#lx\n", ret); - todo_wine ok(!key, "got key %#Ix\n", key); - todo_wine ok(value == 0xdeadbeef, "got value %#Ix\n", value); - todo_wine ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status); + ok(!ret, "got %#lx\n", ret); + ok(!key, "got key %#Ix\n", key); + ok(value == 0xdeadbeef, "got value %#Ix\n", value); + ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status); } else { diff --git a/server/async.c b/server/async.c index e246650ff3f..91f0d87f9df 100644 --- a/server/async.c +++ b/server/async.c @@ -616,6 +616,28 @@ void cancel_process_asyncs( struct process *process ) cancel_async( process, NULL, NULL, 0 ); } +int async_close_obj_handle( struct object *obj, struct process *process, obj_handle_t handle ) +{ + /* Handle a special case when the last object handle in the given process is closed. + * If this is the last object handle overall that is handled in object's close_handle and + * destruction. */ + struct async *async; + + if (obj->handle_count == 1 || get_obj_handle_count( process, obj ) != 1) return 1; + +restart: + LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) + { + if (async->terminated || async->canceled || get_fd_user( async->fd ) != obj) continue; + if (!async->completion || !async->data.apc_context || async->event) continue; + + async->canceled = 1; + fd_cancel_async( async->fd, async ); + goto restart; + } + return 1; +} + void cancel_terminating_thread_asyncs( struct thread *thread ) { struct async *async; diff --git a/server/file.h b/server/file.h index df3b3b3ae4a..1965626dc0e 100644 --- a/server/file.h +++ b/server/file.h @@ -257,6 +257,7 @@ extern struct thread *async_get_thread( struct async *async ); extern struct async *find_pending_async( struct async_queue *queue ); extern void cancel_process_asyncs( struct process *process ); extern void cancel_terminating_thread_asyncs( struct thread *thread ); +extern int async_close_obj_handle( struct object *obj, struct process *process, obj_handle_t handle ); static inline void init_async_queue( struct async_queue *queue ) { diff --git a/server/handle.c b/server/handle.c index 71b23093f62..48b5d8101bb 100644 --- a/server/handle.c +++ b/server/handle.c @@ -522,6 +522,21 @@ obj_handle_t find_inherited_handle( struct process *process, const struct object return 0; } +/* return number of open handles to the object in the process */ +unsigned int get_obj_handle_count( struct process *process, const struct object *obj ) +{ + struct handle_table *table = process->handles; + struct handle_entry *ptr; + unsigned int count = 0; + int i; + + if (!table) return 0; + + for (i = 0, ptr = table->entries; i <= table->last; i++, ptr++) + if (ptr->ptr == obj) ++count; + return count; +} + /* get/set the handle reserved flags */ /* return the old flags (or -1 on error) */ static int set_handle_flags( struct process *process, obj_handle_t handle, int mask, int flags ) diff --git a/server/handle.h b/server/handle.h index ac3104dc003..1d02e040258 100644 --- a/server/handle.h +++ b/server/handle.h @@ -48,6 +48,7 @@ extern obj_handle_t open_object( struct process *process, obj_handle_t parent, u const struct object_ops *ops, const struct unicode_str *name, unsigned int attr ); extern obj_handle_t find_inherited_handle( struct process *process, const struct object_ops *ops ); +extern unsigned int get_obj_handle_count( struct process *process, const struct object *obj ); extern void close_process_handles( struct process *process ); extern struct handle_table *alloc_handle_table( struct process *process, int count ); extern struct handle_table *copy_handle_table( struct process *process, struct process *parent, diff --git a/server/sock.c b/server/sock.c index b02b1c75b5e..16769fc2b4b 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1658,8 +1658,7 @@ static int sock_close_handle( struct object *obj, struct process *process, obj_h if (signaled) complete_async_poll( poll_req, STATUS_SUCCESS ); } } - - return 1; + return async_close_obj_handle( obj, process, handle ); } static void sock_destroy( struct object *obj ) From 6c95f0062cd2683e3bbff37d2c42914e925826ff Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 14 Jul 2023 20:21:50 -0600 Subject: [PATCH 018/758] server: Cancel pipe asyncs when the last handle in process is closed. (cherry picked from commit 03c1930b74e9a41da84fd712a3ebdbb4f7fcbcb5) --- dlls/ntdll/tests/pipe.c | 5 ++++- server/named_pipe.c | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index 70aa1ebda95..30f4b6f1405 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -3067,9 +3067,12 @@ static void test_async_cancel_on_handle_close(void) ok(bret, "failed, error %lu.\n", GetLastError()); CloseHandle(read); + /* Canceled asyncs with completion port and no event do not update IOSB before removing completion. */ + todo_wine_if(other_process && tests[i].apc_context && !tests[i].event) ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + if (other_process && tests[i].apc_context && !tests[i].event) - todo_wine test_queued_completion(port, &io, STATUS_CANCELLED, 0); + test_queued_completion(port, &io, STATUS_CANCELLED, 0); else test_no_queued_completion(port); diff --git a/server/named_pipe.c b/server/named_pipe.c index 6679f4cc3a0..5c486093c76 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -183,7 +183,7 @@ static const struct object_ops pipe_server_ops = NULL, /* unlink_name */ pipe_server_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_server_destroy /* destroy */ }; @@ -229,7 +229,7 @@ static const struct object_ops pipe_client_ops = NULL, /* unlink_name */ no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ - no_close_handle, /* close_handle */ + async_close_obj_handle, /* close_handle */ pipe_end_destroy /* destroy */ }; From 9de909c4ae49b650d9b593b5fa4c4ff9198242b1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 20 Jul 2023 20:45:34 -0600 Subject: [PATCH 019/758] amd_ags_x64, atiadlxx: Bump driver version. --- dlls/amd_ags_x64/amd_ags_x64_main.c | 11 +++++++---- dlls/atiadlxx/atiadlxx_main.c | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 3706e21915c..facea4ff622 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -22,6 +22,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(amd_ags); +static const char driver_version[] = "23.10.01.45-230626a-393367C-AMD-Software-Adrenalin-Edition"; +static const char radeon_version[] = "23.7.1"; + enum amd_ags_version { AMD_AGS_VERSION_5_1_1, @@ -586,8 +589,8 @@ AGSReturnCode WINAPI agsInit(AGSContext **context, const AGSConfiguration *confi gpu_info->agsVersionMajor = amd_ags_info[object->version].major; gpu_info->agsVersionMinor = amd_ags_info[object->version].minor; gpu_info->agsVersionPatch = amd_ags_info[object->version].patch; - gpu_info->driverVersion = "21.30.25.05-211005a-372402E-RadeonSoftware"; - gpu_info->radeonSoftwareVersion = "21.10.2"; + gpu_info->driverVersion = driver_version; + gpu_info->radeonSoftwareVersion = radeon_version; gpu_info->numDevices = object->device_count; gpu_info->devices = object->devices; @@ -621,8 +624,8 @@ AGSReturnCode WINAPI agsInitialize(int ags_version, const AGSConfiguration *conf } memset(gpu_info, 0, sizeof(*gpu_info)); - gpu_info->driverVersion = "21.30.25.05-211005a-372402E-RadeonSoftware"; - gpu_info->radeonSoftwareVersion = "21.10.2"; + gpu_info->driverVersion = driver_version; + gpu_info->radeonSoftwareVersion = radeon_version; gpu_info->numDevices = object->device_count; gpu_info->devices = object->devices; diff --git a/dlls/atiadlxx/atiadlxx_main.c b/dlls/atiadlxx/atiadlxx_main.c index 21dfbe71096..7104bcfa723 100644 --- a/dlls/atiadlxx/atiadlxx_main.c +++ b/dlls/atiadlxx/atiadlxx_main.c @@ -172,15 +172,15 @@ typedef struct ADLDisplayMap } ADLDisplayMap, *LPADLDisplayMap; static const ADLVersionsInfo version = { - "22.20.19.16-221003a-384125E-AMD-Software-Adrenalin-Edition", + "23.10.01.45-230626a-393367C-AMD-Software-Adrenalin-Edition", "", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; static const ADLVersionsInfoX2 version2 = { - "22.20.19.16-221003a-384125E-AMD-Software-Adrenalin-Edition", + "23.10.01.45-230626a-393367C-AMD-Software-Adrenalin-Edition", "", - "22.10.1", + "23.7.1", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; From 9063b377b0627db896e0385e9dd59cf12f50597b Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Fri, 14 Jul 2023 18:02:31 -0300 Subject: [PATCH 020/758] user32: Add hotpatch prologue to RegisterRawInputDevices. re4_tweaks relies on this function being hotpatchable [1]. [1]: https://github.com/nipkownix/re4_tweaks/blob/7cfcf5c4272f7d68d177661ffe84e9d1e0d2f383/dllmain/input.cpp#L903 (cherry picked from commit fb296929f98e93e5ba08ff4585c09886076da5a9) CW-Bug-Id: #22401 --- dlls/user32/user32.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index 21962768dcc..aa96251e4da 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -611,7 +611,7 @@ @ stub RegisterNetworkCapabilities @ stdcall RegisterPointerDeviceNotifications(long long) @ stdcall RegisterPowerSettingNotification(long ptr long) -@ stdcall RegisterRawInputDevices(ptr long long) NtUserRegisterRawInputDevices +@ stdcall -import RegisterRawInputDevices(ptr long long) NtUserRegisterRawInputDevices @ stdcall RegisterServicesProcess(long) @ stdcall RegisterShellHookWindow (long) @ stdcall RegisterSuspendResumeNotification(long long) From f53fa8132c690f171972288169c94b92e651e509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Jul 2023 10:22:33 +0200 Subject: [PATCH 021/758] dinput: Unacquire only DISCL_FOREGROUND devices on foreground changes. Fixes broken input in Final Fantasy XIII after main window focus loss. (cherry picked from commit 4151acb4e8f04240c2dfceb8830ba7c34f5bf251) CW-Bug-Id: #22203 --- dlls/dinput/dinput_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index f2d75a14d2e..c99729a3770 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -144,7 +144,7 @@ static LRESULT CALLBACK input_thread_ll_hook_proc( int code, WPARAM wparam, LPAR return skip ? 1 : CallNextHookEx( 0, code, wparam, lparam ); } -static void dinput_unacquire_window_devices( HWND window ) +static void handle_foreground_lost( HWND window ) { struct dinput_device *impl, *next; @@ -152,7 +152,7 @@ static void dinput_unacquire_window_devices( HWND window ) LIST_FOR_EACH_ENTRY_SAFE( impl, next, &acquired_device_list, struct dinput_device, entry ) { - if (window != impl->win) continue; + if (!(impl->dwCoopLevel & DISCL_FOREGROUND) || window != impl->win) continue; TRACE( "%p window is not foreground - unacquiring %p\n", impl->win, impl ); dinput_device_internal_unacquire( &impl->IDirectInputDevice8W_iface, STATUS_UNACQUIRED ); } @@ -327,7 +327,7 @@ static LRESULT WINAPI di_em_win_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPAR input_thread_update_device_list( state ); break; case NOTIFY_FOREGROUND_LOST: - dinput_unacquire_window_devices( (HWND)lparam ); + handle_foreground_lost( (HWND)lparam ); break; } From df2606689a51737200cbac94a30a63a368747459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Jul 2023 20:53:07 +0200 Subject: [PATCH 022/758] winegstreamer: Free the media source work queue outside of the CS. Possibly fixing some rare deadlock with MSFS. CW-Bug-Id: #22377 --- dlls/winegstreamer/media_source.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 6c00f103662..172e775efc9 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1247,6 +1247,7 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) if (!ref) { IMFMediaSource_Shutdown(iface); + MFUnlockWorkQueue(source->async_commands_queue); IMFPresentationDescriptor_Release(source->pres_desc); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); @@ -1450,8 +1451,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) } free(source->streams); - MFUnlockWorkQueue(source->async_commands_queue); - LeaveCriticalSection(&source->cs); return S_OK; From 5034527cb3573fadb11587949ba41ab4d7ab62be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Jul 2023 21:56:32 +0200 Subject: [PATCH 023/758] fixup! winegstreamer: Allow concurrent wait_on_sample in the media source. CW-Bug-Id: #22377 --- dlls/winegstreamer/media_source.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 172e775efc9..c9ee6f227e6 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -44,11 +44,12 @@ struct media_stream LONG token_queue_count; LONG token_queue_cap; - CRITICAL_SECTION cs; - DWORD stream_id; BOOL active; BOOL eos; + + DWORD busy; + CONDITION_VARIABLE cond; }; enum source_async_op @@ -538,17 +539,12 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) TRACE("%p, %p\n", stream, token); - EnterCriticalSection(&stream->cs); - - if (!stream->wg_stream) - { - LeaveCriticalSection(&stream->cs); - return; - } - + stream->busy = TRUE; LeaveCriticalSection(&source->cs); ret = wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer); EnterCriticalSection(&source->cs); + stream->busy = FALSE; + WakeConditionVariable(&stream->cond); if (source->state == SOURCE_SHUTDOWN) WARN("media source has been shutdown, returning\n"); @@ -560,8 +556,6 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); dispatch_end_of_presentation(source); } - - LeaveCriticalSection(&stream->cs); } static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -721,8 +715,6 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) IMFStreamDescriptor_Release(stream->descriptor); IMFMediaEventQueue_Release(stream->event_queue); flush_token_queue(stream, FALSE); - stream->cs.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&stream->cs); free(stream); } @@ -887,9 +879,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, object->eos = FALSE; object->wg_stream = wg_parser_get_stream(wg_parser, id); - InitializeCriticalSection(&object->cs); - object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); - TRACE("Created stream object %p.\n", object); *out = object; @@ -1428,10 +1417,8 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_stream *stream = source->streams[i]; wg_parser_stream_disable(stream->wg_stream); - - EnterCriticalSection(&stream->cs); - stream->wg_stream = NULL; - LeaveCriticalSection(&stream->cs); + while (stream->busy) + SleepConditionVariableCS(&stream->cond, &source->cs, INFINITE); } wg_parser_disconnect(source->wg_parser); From 5da3701c6fe8352d55a0292df7087ebb661b72c1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 28 Jul 2023 15:22:42 -0600 Subject: [PATCH 024/758] winhttp: Set FILE_SKIP_COMPLETION_PORT_ON_SUCCESS on sockets. (cherry picked from commit 96b6bf6111e841c2a0791c5d99045a1ef7062f48) CW-Bug-Id: #22517 --- dlls/winhttp/net.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index c10438df624..251ac389e60 100644 --- a/dlls/winhttp/net.c +++ b/dlls/winhttp/net.c @@ -26,6 +26,7 @@ #include "ws2tcpip.h" #include "winhttp.h" #include "schannel.h" +#include "winternl.h" #include "wine/debug.h" #include "winhttp_private.h" @@ -224,6 +225,8 @@ DWORD netconn_create( struct hostdata *host, const struct sockaddr_storage *sock free( conn ); return ret; } + if (!SetFileCompletionNotificationModes( (HANDLE)(UINT_PTR)conn->socket, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS )) + ERR( "SetFileCompletionNotificationModes failed.\n" ); switch (conn->sockaddr.ss_family) { From 3a0aaafe5724b5b0c795560af2fe33d16c73fc84 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 28 Jul 2023 15:35:02 -0600 Subject: [PATCH 025/758] winhttp: Skip unexpected completions in netconn_wait_overlapped_result(). (cherry picked from commit 3b69baaee85a99d3091b6fffdad0d8b8a95df6ec) CW-Bug-Id: #22517 --- dlls/winhttp/net.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index 251ac389e60..134227da31b 100644 --- a/dlls/winhttp/net.c +++ b/dlls/winhttp/net.c @@ -57,15 +57,16 @@ BOOL netconn_wait_overlapped_result( struct netconn *conn, WSAOVERLAPPED *ovr, D OVERLAPPED *completion_ovr; ULONG_PTR key; - if (!GetQueuedCompletionStatus( conn->port, len, &key, &completion_ovr, INFINITE )) + while (1) { - WARN( "GetQueuedCompletionStatus failed, err %lu.\n", GetLastError() ); - return FALSE; - } - if ((key != conn->socket && conn->socket != -1) || completion_ovr != (OVERLAPPED *)ovr) - { - ERR( "Unexpected completion key %Ix, overlapped %p.\n", key, completion_ovr ); - return FALSE; + if (!GetQueuedCompletionStatus( conn->port, len, &key, &completion_ovr, INFINITE )) + { + WARN( "GetQueuedCompletionStatus failed, err %lu.\n", GetLastError() ); + return FALSE; + } + if (completion_ovr == (OVERLAPPED *)ovr && (key == conn->socket || conn->socket == -1)) + break; + ERR( "Unexpected completion key %Ix, completion ovr %p, ovr %p.\n", key, completion_ovr, ovr ); } return TRUE; } From aa8fead1a0ea78a58861410d6dbc0952ecd3ddda Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 9 Jun 2023 18:40:56 +0800 Subject: [PATCH 026/758] fshack: winex11.drv: Use width and height to check if the current mode is a landscape mode. Check the ratio of dmPelsWidth to dmPelsHeight to determine whether the host is currently in portrait or landscape orientation. DMDO_DEFAULT is the natural orientation of the device, which isn't necessarily a landscape mode. CW-Bug-Id: #21774 --- dlls/winex11.drv/fs.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c index 74a87007847..1c135fbebed 100644 --- a/dlls/winex11.drv/fs.c +++ b/dlls/winex11.drv/fs.c @@ -274,7 +274,7 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN { UINT i, j, max_count, real_mode_count, resolutions = 0; DEVMODEW *real_modes, *real_mode, mode_host = {0}; - BOOL additional_modes = FALSE; + BOOL additional_modes = FALSE, landscape; const char *env; *mode_count = 0; @@ -291,6 +291,11 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN return; } + /* Check the ratio of dmPelsWidth to dmPelsHeight to determine whether the host is currently in + * portrait or landscape orientation. DMDO_DEFAULT is the natural orientation of the device, + * which isn't necessarily a landscape mode */ + landscape = mode_host.dmPelsWidth >= mode_host.dmPelsHeight; + /* Add the current mode early, in case we have to limit */ modes_append( *modes, mode_count, &resolutions, &mode_host ); @@ -306,8 +311,7 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN if (!additional_modes && fs_monitor_sizes[i].additional) continue; - if (mode_host.dmDisplayOrientation == DMDO_DEFAULT || - mode_host.dmDisplayOrientation == DMDO_180) + if (landscape) { mode.dmPelsWidth = fs_monitor_sizes[i].size.cx; mode.dmPelsHeight = fs_monitor_sizes[i].size.cy; From ee95a94b993550e12befe8b84b2c171a22441de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Sun, 4 Dec 2022 00:03:50 +0100 Subject: [PATCH 027/758] mfmediaengine: Add support for inserting audio effects. --- dlls/mfmediaengine/main.c | 57 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 4f99513e938..d9b3bd19547 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "winerror.h" #define COBJMACROS #include @@ -146,6 +147,11 @@ struct media_engine IMFPresentationDescriptor *pd; } presentation; struct + { + IUnknown *audio; + BOOL audio_optional; + } effects; + struct { LONGLONG pts; SIZE size; @@ -999,6 +1005,27 @@ static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresen return S_OK; } +static HRESULT media_engine_create_audio_effect(struct media_engine *engine, IMFTopologyNode **node) +{ + HRESULT hr; + + *node = NULL; + + if (!engine->effects.audio) + return S_OK; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, node))) + return hr; + + IMFTopologyNode_SetObject(*node, (IUnknown *)engine->effects.audio); + IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); + + IUnknown_Release(engine->effects.audio); + engine->effects.audio = NULL; + + return hr; +} + static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1169,7 +1196,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (SUCCEEDED(hr = MFCreateTopology(&topology))) { - IMFTopologyNode *sar_node = NULL, *audio_src = NULL; + IMFTopologyNode *sar_node = NULL, *audio_src = NULL, *audio_effect = NULL; IMFTopologyNode *grabber_node = NULL, *video_src = NULL; if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) @@ -1183,7 +1210,18 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (FAILED(hr = media_engine_create_audio_renderer(engine, &sar_node))) WARN("Failed to create audio renderer node, hr %#lx.\n", hr); - if (sar_node && audio_src) + if (FAILED(media_engine_create_audio_effect(engine, &audio_effect))) + WARN("Failed to create audio effect node, hr %#lx.\n", hr); + + if (sar_node && audio_src && audio_effect) + { + IMFTopology_AddNode(topology, audio_src); + IMFTopology_AddNode(topology, sar_node); + IMFTopology_AddNode(topology, audio_effect); + IMFTopologyNode_ConnectOutput(audio_src, 0, audio_effect, 0); + IMFTopologyNode_ConnectOutput(audio_effect, 0, sar_node, 0); + } + else if (sar_node && audio_src) { IMFTopology_AddNode(topology, audio_src); IMFTopology_AddNode(topology, sar_node); @@ -1192,6 +1230,8 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (sar_node) IMFTopologyNode_Release(sar_node); + if (audio_effect) + IMFTopologyNode_Release(audio_effect); if (audio_src) IMFTopologyNode_Release(audio_src); } @@ -2566,9 +2606,20 @@ static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IU static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { + struct media_engine *impl = impl_from_IMFMediaEngineEx(iface); FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); - return E_NOTIMPL; + if (!effect) + return E_POINTER; + + if (impl->effects.audio) + return MF_E_INVALIDREQUEST; + + impl->effects.audio = effect; + IUnknown_AddRef(impl->effects.audio); + impl->effects.audio_optional = is_optional; + + return S_OK; } static HRESULT WINAPI media_engine_RemoveAllEffects(IMFMediaEngineEx *iface) From 5bb1f1fa49afb44a9f4cba896265fe521b939b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 5 Dec 2022 23:46:20 +0100 Subject: [PATCH 028/758] mfmediaengine: Add support for inserting video effects. --- dlls/mfmediaengine/main.c | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index d9b3bd19547..1d5b4a923ed 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -150,6 +150,8 @@ struct media_engine { IUnknown *audio; BOOL audio_optional; + IUnknown *video; + BOOL video_optional; } effects; struct { @@ -1026,6 +1028,27 @@ static HRESULT media_engine_create_audio_effect(struct media_engine *engine, IMF return hr; } +static HRESULT media_engine_create_video_effect(struct media_engine *engine, IMFTopologyNode **node) +{ + HRESULT hr; + + *node = NULL; + + if (!engine->effects.video) + return S_OK; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, node))) + return hr; + + IMFTopologyNode_SetObject(*node, (IUnknown *)engine->effects.video); + IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); + + IUnknown_Release(engine->effects.video); + engine->effects.video = NULL; + + return hr; +} + static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1197,7 +1220,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (SUCCEEDED(hr = MFCreateTopology(&topology))) { IMFTopologyNode *sar_node = NULL, *audio_src = NULL, *audio_effect = NULL; - IMFTopologyNode *grabber_node = NULL, *video_src = NULL; + IMFTopologyNode *grabber_node = NULL, *video_src = NULL, *video_effect = NULL; if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) IMFTopology_SetUINT32(topology, &MF_LOW_LATENCY, TRUE); @@ -1244,7 +1267,18 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (FAILED(hr = media_engine_create_video_renderer(engine, &grabber_node))) WARN("Failed to create video grabber node, hr %#lx.\n", hr); - if (grabber_node && video_src) + if (FAILED(media_engine_create_video_effect(engine, &video_effect))) + WARN("Failed to create video effect node, hr %#lx.\n", hr); + + if (grabber_node && video_src && video_effect) + { + IMFTopology_AddNode(topology, video_src); + IMFTopology_AddNode(topology, grabber_node); + IMFTopology_AddNode(topology, video_effect); + IMFTopologyNode_ConnectOutput(video_src, 0, video_effect, 0); + IMFTopologyNode_ConnectOutput(video_effect, 0, grabber_node, 0); + } + else if (grabber_node && video_src) { IMFTopology_AddNode(topology, video_src); IMFTopology_AddNode(topology, grabber_node); @@ -2599,9 +2633,20 @@ static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *pr static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { + struct media_engine *impl = impl_from_IMFMediaEngineEx(iface); FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); - return E_NOTIMPL; + if (!effect) + return E_POINTER; + + if (impl->effects.video) + return MF_E_INVALIDREQUEST; + + impl->effects.video = effect; + IUnknown_AddRef(impl->effects.video); + impl->effects.video_optional = is_optional; + + return S_OK; } static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) From 3637d94facb9add015cd08882dafe8adf123a932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Mar 2023 21:28:03 +0100 Subject: [PATCH 029/758] mfmediaengine: Return S_OK in SetBalance(). --- dlls/mfmediaengine/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 1d5b4a923ed..e065080fdfa 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -2517,7 +2517,7 @@ static HRESULT WINAPI media_engine_SetBalance(IMFMediaEngineEx *iface, double ba { FIXME("%p, %f stub.\n", iface, balance); - return E_NOTIMPL; + return S_OK; } static BOOL WINAPI media_engine_IsPlaybackRateSupported(IMFMediaEngineEx *iface, double rate) From 748b2708768b10e5c1cd2a23d97777118dd02123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Mar 2023 21:28:24 +0100 Subject: [PATCH 030/758] Revert "Revert "mfmediaengine: Pass volume changes to media session."" This reverts commit 602e0ee8b30b32279dcc349c538401267e7acce8. --- dlls/mfmediaengine/main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index e065080fdfa..837aaaae106 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -843,6 +843,23 @@ static void media_engine_get_frame_size(struct media_engine *engine, IMFTopology IMFMediaType_Release(media_type); } +static void media_engine_apply_volume(const struct media_engine *engine) +{ + IMFSimpleAudioVolume *sa_volume; + HRESULT hr; + + if (!engine->session) + return; + + if (FAILED(MFGetService((IUnknown *)engine->session, &MR_POLICY_VOLUME_SERVICE, &IID_IMFSimpleAudioVolume, (void **)&sa_volume))) + return; + + if (FAILED(hr = IMFSimpleAudioVolume_SetMasterVolume(sa_volume, engine->volume))) + WARN("Failed to set master volume, hr %#lx.\n", hr); + + IMFSimpleAudioVolume_Release(sa_volume); +} + static HRESULT WINAPI media_engine_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IMFAsyncCallback) || @@ -926,6 +943,8 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface EnterCriticalSection(&engine->cs); + media_engine_apply_volume(engine); + engine->ready_state = MF_MEDIA_ENGINE_READY_HAVE_METADATA; media_engine_get_frame_size(engine, topology); @@ -2086,6 +2105,7 @@ static HRESULT WINAPI media_engine_SetVolume(IMFMediaEngineEx *iface, double vol else if (volume != engine->volume) { engine->volume = volume; + media_engine_apply_volume(engine); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE, 0, 0); } LeaveCriticalSection(&engine->cs); From 9f601543243d7f7ca714230493be7f6d86a14dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Mar 2023 21:58:34 +0100 Subject: [PATCH 031/758] mf: Don't try to clone non existent topo connections. --- dlls/mf/topology.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/mf/topology.c b/dlls/mf/topology.c index 25a00708100..11ddf573f06 100644 --- a/dlls/mf/topology.c +++ b/dlls/mf/topology.c @@ -712,7 +712,11 @@ static HRESULT WINAPI topology_CloneFrom(IMFTopology *iface, IMFTopology *src) for (j = 0; j < outputs->count; ++j) { DWORD input_index = outputs->streams[j].connection_stream; - TOPOID id = outputs->streams[j].connection->id; + TOPOID id; + + if (!outputs->streams[j].connection) + continue; + id = outputs->streams[j].connection->id; /* Skip node lookup in destination topology, assuming same node order. */ if (SUCCEEDED(hr = topology_get_node_by_id(topology, id, &node))) From 4a37623c037cc574abbb65f93ea42284531be7ee Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 4 May 2023 13:33:48 -0600 Subject: [PATCH 032/758] ntdll: HACK: Add WINE_RAM_REPORTING_BIAS option. CW-Bug-Id: #22241 --- dlls/ntdll/unix/loader.c | 6 ++++++ dlls/ntdll/unix/system.c | 8 ++++++++ dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 1 + 4 files changed, 16 insertions(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index c30e62222c8..dc9283bc7eb 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2284,12 +2284,18 @@ BOOL localsystem_sid; BOOL high_dll_addresses; BOOL simulate_writecopy; SIZE_T kernel_stack_size = 0x100000; +long long ram_reporting_bias; static void hacks_init(void) { static const char upc_exe[] = "Ubisoft Game Launcher\\upc.exe"; const char *env_str, *sgi; + if ((env_str = getenv("WINE_RAM_REPORTING_BIAS"))) + { + ram_reporting_bias = atoll(env_str) * 1024 * 1024; + ERR( "HACK: ram_reporting_bias %lldMB.\n", ram_reporting_bias / (1024 * 1024) ); + } env_str = getenv("WINE_SIMULATE_ASYNC_READ"); if (env_str) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 16fde91ea7c..76a47f1932a 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2042,7 +2042,15 @@ static void get_performance_info( SYSTEM_PERFORMANCE_INFORMATION *info ) mem_available = value * 1024; } fclose(fp); + totalram -= min( totalram, ram_reporting_bias ); if (mem_available) freeram = mem_available; + if ((long long)freeram >= ram_reporting_bias) freeram -= ram_reporting_bias; + else + { + long long bias = ram_reporting_bias - freeram; + freeswap -= min( bias, freeswap ); + freeram = 0; + } } } #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || \ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index d54d802e0cb..759483ca3ba 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -159,6 +159,7 @@ extern BOOL no_priv_elevation DECLSPEC_HIDDEN; extern BOOL localsystem_sid DECLSPEC_HIDDEN; extern BOOL high_dll_addresses DECLSPEC_HIDDEN; extern BOOL simulate_writecopy DECLSPEC_HIDDEN; +extern long long ram_reporting_bias; extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; extern void init_startup_info(void) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index ea314dbe1d5..a7f0f2dc4c3 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2987,6 +2987,7 @@ void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info, BOOL wow64 ) if (!sysinfo(&sinfo)) { ULONG64 total = (ULONG64)sinfo.totalram * sinfo.mem_unit; + total -= min(total, ram_reporting_bias); info->MmHighestPhysicalPage = max(1, total / page_size); } #elif defined(_SC_PHYS_PAGES) From 3dcfd93fb0d772002c875591f9e95e3f2ecb8656 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 10 May 2023 13:17:26 -0600 Subject: [PATCH 033/758] ntdll: Add virtstat debug channel and log memory allocation statistics. This is solely for debugging purposes and does nothing unless +virtstat logging is enabled. CW-Bug-Id: #22045 CW-Bug-Id: #22247 --- dlls/ntdll/unix/virtual.c | 56 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index a7f0f2dc4c3..b62968cb26d 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -80,6 +80,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(virtual); WINE_DECLARE_DEBUG_CHANNEL(module); WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges); +WINE_DECLARE_DEBUG_CHANNEL(virtstat); struct preload_info { @@ -1172,6 +1173,53 @@ static void VIRTUAL_Dump(void) #endif +static void dump_memory_statistics(void) +{ + struct file_view *view; + SIZE_T anon_reserved = 0, anon_committed = 0, mapped = 0, mapped_committed = 0, marked_native = 0; + SIZE_T size, c_size; + char *base; + BYTE vprot; + + if (!TRACE_ON(virtstat)) return; + + WINE_RB_FOR_EACH_ENTRY( view, &views_tree, struct file_view, entry ) + { + if (view->protect & VPROT_NATIVE) + { + marked_native += view->size; + continue; + } + base = view->base; + c_size = 0; + while (base != (char *)view->base + view->size) + { + size = get_vprot_range_size( base, (char *)view->base + view->size - base, VPROT_COMMITTED, &vprot ); + if (vprot & VPROT_COMMITTED) c_size += size; + base += size; + } + if (is_view_valloc( view )) + { + anon_reserved += view->size; + anon_committed += c_size; + } + else + { + mapped += view->size; + mapped_committed += c_size; + } + } + + anon_reserved /= 1024 * 1024; + anon_committed /= 1024 * 1024; + mapped /= 1024 * 1024; + mapped_committed /= 1024 * 1024; + marked_native /= 1024 * 1024; + TRACE_(virtstat)( "Total: res %lu, comm %lu; Anon: res %lu, comm %lu, marked Unix %lu.\n", + anon_reserved + mapped, anon_committed + mapped_committed, anon_reserved, anon_committed, + marked_native ); +} + /*********************************************************************** * find_view * @@ -4147,7 +4195,11 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ } } - if (!status) VIRTUAL_DEBUG_DUMP_VIEW( view ); + if (!status) + { + VIRTUAL_DEBUG_DUMP_VIEW( view ); + dump_memory_statistics(); + } server_leave_uninterrupted_section( &virtual_mutex, &sigset ); @@ -4514,6 +4566,8 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si status = STATUS_INVALID_PARAMETER; } + dump_memory_statistics(); + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); return status; } From 4ca034b37fd34b4a27546fd09685dd3d60aede7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:35:41 +0200 Subject: [PATCH 034/758] Revert "winex11.drv: Send missed KEYUP events on KeymapNotify." This reverts commit 26f41550b292ce1784701c08711dbcb488f20482. --- dlls/winex11.drv/event.c | 2 -- dlls/winex11.drv/keyboard.c | 43 ++----------------------------------- dlls/winex11.drv/mouse.c | 2 -- dlls/winex11.drv/x11drv.h | 1 - 4 files changed, 2 insertions(+), 46 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 760177d0622..2aaf8e482db 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -827,8 +827,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE; - x11drv_thread_data()->keymapnotify_hwnd = hwnd; - /* Focus was just restored but it can be right after super was * pressed and gnome-shell needs a bit of time to respond and * toggle the activity view. If we grab the cursor right away diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index b51546a6340..db001696a14 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1206,19 +1206,11 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) int i, j; BYTE keystate[256]; WORD vkey; - DWORD flags; - KeyCode keycode; - HWND keymapnotify_hwnd; BOOL changed = FALSE; struct { WORD vkey; - WORD scan; WORD pressed; } keys[256]; - struct x11drv_thread_data *thread_data = x11drv_thread_data(); - - keymapnotify_hwnd = thread_data->keymapnotify_hwnd; - thread_data->keymapnotify_hwnd = NULL; if (!get_async_key_state( keystate )) return FALSE; @@ -1233,17 +1225,11 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) { for (j = 0; j < 8; j++) { - keycode = (i * 8) + j; - vkey = keyc2vkey[keycode]; + vkey = keyc2vkey[(i * 8) + j]; /* If multiple keys map to the same vkey, we want to report it as * pressed iff any of them are pressed. */ - if (!keys[vkey & 0xff].vkey) - { - keys[vkey & 0xff].vkey = vkey; - keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; - } - + if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); - x11drv_thread_data()->keymapnotify_hwnd = hwnd; - if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; /* simulate a mouse motion event */ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index f1ded52c2b8..07d821e76a8 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -382,7 +382,6 @@ struct x11drv_thread_data XEvent *current_event; /* event currently being processed */ HWND grab_hwnd; /* window that currently grabs the mouse */ HWND last_focus; /* last window that had focus */ - HWND keymapnotify_hwnd; /* window that should receive modifier release events */ XIM xim; /* input method */ HWND last_xic_hwnd; /* last xic window */ XFontSet font_set; /* international text drawing font set */ From e9a16dc4cad9a8586b4fa6391d71fe94503ef002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:35:47 +0200 Subject: [PATCH 035/758] Revert "winex11.drv: Dump keysyms and translations for all keys." This reverts commit 988a654ba10d7a7eda73f358042eb2b0af17a267. --- dlls/winex11.drv/keyboard.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index db001696a14..97a85f59938 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1474,19 +1474,6 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) for (i = 0; i < syms; i++) { if (!(keysym = keycode_to_keysym (display, keyc, i))) continue; ckey[keyc][i] = keysym_to_char(keysym); - if (TRACE_ON(keyboard)) - { - char buf[32]; - WCHAR bufW[32]; - int len, lenW; - KeySym orig_keysym = keysym; - len = XkbTranslateKeySym(display, &keysym, 0, buf, sizeof(buf), NULL); - lenW = ntdll_umbstowcs(buf, len, bufW, ARRAY_SIZE(bufW)); - if (lenW < ARRAY_SIZE(bufW)) - bufW[lenW] = 0; - TRACE("keycode %u, index %d, orig_keysym 0x%04lx, keysym 0x%04lx, buf %s, bufW %s\n", - keyc, i, orig_keysym, keysym, debugstr_a(buf), debugstr_w(bufW)); - } } } From 1f5046ca76504cc00943407882fbb794c9e539fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:35:52 +0200 Subject: [PATCH 036/758] Revert "winex11.drv: Recognize the keyboard in a locale-independent way." This reverts commit 7649db953ed0b1ae06659315a987ecb97befe39f. --- dlls/winex11.drv/keyboard.c | 64 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 97a85f59938..12bb2b863cb 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1414,35 +1414,6 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) return TRUE; } -/* From the point of view of this function there are two types of - * keys: those for which the mapping to vkey and scancode depends on - * the keyboard layout (i.e., letters, numbers, punctuation) and those - * for which it doesn't (control keys); since this function is used to - * recognize the keyboard layout and map keysyms to vkeys and - * scancodes, we are only concerned about the first type, and map - * everything in the second type to zero. - */ -static char keysym_to_char( KeySym keysym ) -{ - /* Dead keys */ - if (0xfe50 <= keysym && keysym < 0xfed0) - return KEYBOARD_MapDeadKeysym( keysym ); - - /* Control keys (there is nothing allocated below 0xfc00, but I - take some margin in case something is added in the future) */ - if (0xf000 <= keysym && keysym < 0x10000) - return 0; - - /* XFree86 vendor keys */ - if (0x10000000 <= keysym) - return 0; - - /* "Normal" keys: return last octet, because our tables don't have - more than that; it would be better to extend the tables and - compare the whole keysym, but it's a lot of work... */ - return keysym & 0xff; -} - /********************************************************************** * X11DRV_KEYBOARD_DetectLayout * @@ -1473,7 +1444,24 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) /* get data for keycode from X server */ for (i = 0; i < syms; i++) { if (!(keysym = keycode_to_keysym (display, keyc, i))) continue; - ckey[keyc][i] = keysym_to_char(keysym); + /* Allow both one-byte and two-byte national keysyms */ + if ((keysym < 0x8000) && (keysym != ' ')) + { +#ifdef HAVE_XKB + if (!use_xkb || !XkbTranslateKeySym(display, &keysym, 0, &ckey[keyc][i], 1, NULL)) +#endif + { + TRACE("XKB could not translate keysym %04lx\n", keysym); + /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent + * with appropriate ShiftMask and Mode_switch, use XLookupString + * to get character in the local encoding. + */ + ckey[keyc][i] = keysym & 0xFF; + } + } + else { + ckey[keyc][i] = KEYBOARD_MapDeadKeysym(keysym); + } } } @@ -1684,7 +1672,21 @@ void X11DRV_InitKeyboard( Display *display ) int maxlen=0,maxval=-1,ok; for (i=0; i Date: Mon, 23 Jan 2023 21:30:06 +0100 Subject: [PATCH 037/758] win32u: Silence spurious FIXME in NtUserScrollWindowEx. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54357 (cherry picked from commit 8870d51d3e09565463baa11ce7d2b43c9ca252e4) --- dlls/win32u/dce.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c index 773d6e0b059..d7aa75b3039 100644 --- a/dlls/win32u/dce.c +++ b/dlls/win32u/dce.c @@ -1725,7 +1725,7 @@ INT WINAPI NtUserScrollWindowEx( HWND hwnd, INT dx, INT dy, const RECT *rect, TRACE( "%p, %d,%d update_rgn=%p update_rect = %p %s %04x\n", hwnd, dx, dy, update_rgn, update_rect, wine_dbgstr_rect(rect), flags ); TRACE( "clip_rect = %s\n", wine_dbgstr_rect(clip_rect) ); - if (flags & ~(SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE)) + if (flags & ~(SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE | SW_NODCCACHE)) FIXME( "some flags (%04x) are unhandled\n", flags ); rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? From 4df431c8534b35878c957e02b7ccfd3a9b01bbcf Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 12 Dec 2022 18:45:17 +0100 Subject: [PATCH 038/758] user32/tests: Don't assign const variable to other const (gcc 4.7). Signed-off-by: Fabian Maurer (cherry picked from commit 4bc6aad375e552fa5930c65c0e9c434f1848286e) --- dlls/user32/tests/sysparams.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/user32/tests/sysparams.c b/dlls/user32/tests/sysparams.c index 5c199a07769..6ed7755b91b 100644 --- a/dlls/user32/tests/sysparams.c +++ b/dlls/user32/tests/sysparams.c @@ -3123,7 +3123,8 @@ static void test_EnumDisplaySettings(void) { static const DWORD mode_fields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; - static const DWORD setting_fields = mode_fields | DM_POSITION; + static const DWORD setting_fields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | + DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY | DM_POSITION; CHAR primary_adapter[CCHDEVICENAME]; DPI_AWARENESS_CONTEXT ctx = NULL; DWORD err, val, device, mode; From 1611b0c14145e49e83c0afe4b386cb42773e7417 Mon Sep 17 00:00:00 2001 From: Akihiro Sagawa Date: Fri, 20 Jan 2023 23:51:36 +0900 Subject: [PATCH 039/758] user32/tests: Add DBCS WM_CHAR tests for edit control. (cherry picked from commit cbe3a39b647e029df16faf1dcce2958f1ee418d0) --- dlls/user32/tests/edit.c | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/dlls/user32/tests/edit.c b/dlls/user32/tests/edit.c index b70cf2d6e06..0409b3fde7c 100644 --- a/dlls/user32/tests/edit.c +++ b/dlls/user32/tests/edit.c @@ -3366,6 +3366,58 @@ static void test_wordbreak_proc(void) DestroyWindow(hwnd); } +static void test_dbcs_WM_CHAR(void) +{ + WCHAR textW[] = { 0x4e00, 0x4e8c, 0x4e09, 0 }; /* one, two, three */ + unsigned char bytes[7]; + HWND hwnd[2]; + int i; + + WideCharToMultiByte(CP_ACP, 0, textW, -1, (char *)bytes, ARRAY_SIZE(bytes), NULL, NULL); + if (!IsDBCSLeadByte(bytes[0])) + { + skip("Skipping DBCS WM_CHAR test in this codepage\n"); + return; + } + hwnd[0] = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + hwnd[1] = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + + for (i = 0; i < ARRAY_SIZE(hwnd); i++) + { + const unsigned char* p; + WCHAR strW[4]; + char str[7]; + MSG msg; + BOOL r; + int n; + + winetest_push_context("%c", i ? 'W' : 'A'); + + r = SetWindowTextA(hwnd[i], ""); + ok(r, "SetWindowText failed\n"); + + for (p = bytes; *p; p++) + PostMessageA(hwnd[i], WM_CHAR, *p, 1); + + while (PeekMessageA(&msg, hwnd[i], 0, 0, PM_REMOVE)) + DispatchMessageA(&msg); + + n = GetWindowTextW(hwnd[i], strW, ARRAY_SIZE(strW)); + ok(n > 0, "GetWindowTextW failed\n"); + todo_wine ok(!wcscmp(strW, textW), "got %s, expected %s\n", + wine_dbgstr_w(strW), wine_dbgstr_w(textW)); + + n = GetWindowTextA(hwnd[i], str, ARRAY_SIZE(str)); + ok(n > 0, "GetWindowText failed\n"); + todo_wine ok(!strcmp(str, (char*)bytes), "got %s, expected %s\n", + wine_dbgstr_a(str), wine_dbgstr_a((char *)bytes)); + + DestroyWindow(hwnd[i]); + + winetest_pop_context(); + } +} + START_TEST(edit) { BOOL b; @@ -3403,6 +3455,7 @@ START_TEST(edit) test_paste(); test_EM_GETLINE(); test_wordbreak_proc(); + test_dbcs_WM_CHAR(); UnregisterWindowClasses(); } From 3c52ccf4819f73fc120c8413a2f6e9d7c76d78ec Mon Sep 17 00:00:00 2001 From: Akihiro Sagawa Date: Fri, 20 Jan 2023 23:55:59 +0900 Subject: [PATCH 040/758] user32/edit: Fix WM_CHAR handler for double-byte characters. After commit 2aa54a90bd24f6aa422a2eb832a54d9afe585259, a double-byte character is sent to the edit control by double WM_CHAR messages. However, its WM_CHAR handler (ANSI version) can't process a double- byte character properly because the handler converts WM_CHAR to WCHAR one by one. This fix queues the double-byte lead byte as it comes in and then uses it afterwards for the WCHAR conversion. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54337 (cherry picked from commit c385b6475e6ad07227a1ef29593170af1c3cbf99) --- dlls/user32/edit.c | 21 +++++++++++++++++++-- dlls/user32/tests/edit.c | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c index 2e651e455dd..aa74d215673 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -113,6 +113,7 @@ typedef struct should be sent to the first parent. */ HWND hwndListBox; /* handle of ComboBox's listbox or NULL */ INT wheelDeltaRemainder; /* scroll wheel delta left over after scrolling whole lines */ + BYTE lead_byte; /* DBCS lead byte store for WM_CHAR */ /* * only for multi line controls */ @@ -4975,8 +4976,24 @@ LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, B charW = wParam; else { - CHAR charA = wParam; - MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1); + BYTE low = wParam; + DWORD cp = get_input_codepage(); + if (es->lead_byte) + { + char ch[2]; + ch[0] = es->lead_byte; + ch[1] = low; + MultiByteToWideChar(cp, 0, ch, 2, &charW, 1); + } + else if (IsDBCSLeadByteEx(cp, low)) + { + es->lead_byte = low; + result = 1; + break; + } + else + MultiByteToWideChar(cp, 0, (char *)&low, 1, &charW, 1); + es->lead_byte = 0; } if (es->hwndListBox) diff --git a/dlls/user32/tests/edit.c b/dlls/user32/tests/edit.c index 0409b3fde7c..78328ee1729 100644 --- a/dlls/user32/tests/edit.c +++ b/dlls/user32/tests/edit.c @@ -3404,12 +3404,12 @@ static void test_dbcs_WM_CHAR(void) n = GetWindowTextW(hwnd[i], strW, ARRAY_SIZE(strW)); ok(n > 0, "GetWindowTextW failed\n"); - todo_wine ok(!wcscmp(strW, textW), "got %s, expected %s\n", + ok(!wcscmp(strW, textW), "got %s, expected %s\n", wine_dbgstr_w(strW), wine_dbgstr_w(textW)); n = GetWindowTextA(hwnd[i], str, ARRAY_SIZE(str)); ok(n > 0, "GetWindowText failed\n"); - todo_wine ok(!strcmp(str, (char*)bytes), "got %s, expected %s\n", + ok(!strcmp(str, (char*)bytes), "got %s, expected %s\n", wine_dbgstr_a(str), wine_dbgstr_a((char *)bytes)); DestroyWindow(hwnd[i]); From e95da9deb1f5cf0767fce8149e893d2f0ac14134 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Tue, 20 Dec 2022 15:33:56 +0100 Subject: [PATCH 041/758] user32: GetClipboardData() should set last error when the format is not found. This has the benefit of indicating why GetClipboardData() failed and of matching the Windows 11 behavior. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54190 (cherry picked from commit 25d6c1a3b497c1530b4ed2c769ec5728b7c006bc) --- dlls/user32/tests/clipboard.c | 8 ++++++-- dlls/win32u/clipboard.c | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index 3131c2e0e8c..7414af996fb 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -986,7 +986,9 @@ static void test_synthesized(void) SetLastError(0xdeadbeef); data = GetClipboardData( CF_TEXT ); - ok(GetLastError() == 0xdeadbeef, "bad last error %ld\n", GetLastError()); + ok(GetLastError() == ERROR_NOT_FOUND /* win11 */ || + broken(GetLastError() == 0xdeadbeef), + "bad last error %ld\n", GetLastError()); ok(!data, "GetClipboardData() should have returned NULL\n"); r = CloseClipboard(); @@ -1587,7 +1589,9 @@ static void test_handles( HWND hwnd ) SetLastError( 0xdeadbeef ); data = GetClipboardData( CF_RIFF ); - ok( GetLastError() == 0xdeadbeef, "unexpected last error %ld\n", GetLastError() ); + ok( GetLastError() == ERROR_NOT_FOUND /* win11 */ || + broken(GetLastError() == 0xdeadbeef), + "unexpected last error %ld\n", GetLastError() ); ok( !data, "wrong data %p\n", data ); h = SetClipboardData( CF_PRIVATEFIRST + 7, htext4 ); diff --git a/dlls/win32u/clipboard.c b/dlls/win32u/clipboard.c index d53cd966d36..6cf484a56ca 100644 --- a/dlls/win32u/clipboard.c +++ b/dlls/win32u/clipboard.c @@ -720,7 +720,11 @@ HANDLE WINAPI NtUserGetClipboardData( UINT format, struct get_clipboard_params * params->data_size = size; return 0; } - if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0; /* no such format */ + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + RtlSetLastWin32Error( ERROR_NOT_FOUND ); /* no such format */ + return 0; + } if (status) { RtlSetLastWin32Error( RtlNtStatusToDosError( status )); From 8df233350668e655472ee53e5d77811914f7f07d Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 21 Dec 2022 16:11:31 +0100 Subject: [PATCH 042/758] user32/tests: Use wine_dbgstr_an() to trace malformed Unicode strings. Using wine_dbgstr_wn() causes out-of-bounds memory accesses when given Unicode strings with odd sizes, most obviously 1 byte strings. Also trace the expected Ansi string for round-trip tests. (cherry picked from commit 47d492742477e5d0e94b2efda91ef7b405d34300) --- dlls/user32/tests/clipboard.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index 7414af996fb..09f01b7481a 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -2269,7 +2269,7 @@ static void test_string_data(void) memcpy( bufferW, test_data[i].strW, test_data[i].len ); bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; ok( !memcmp( data, bufferW, test_data[i].len ), - "wrong data %s\n", wine_dbgstr_wn( data, (test_data[i].len + 1) / sizeof(WCHAR) )); + "wrong data %s\n", wine_dbgstr_an( data, test_data[i].len )); } r = CloseClipboard(); ok( r, "gle %ld\n", GetLastError() ); @@ -2314,8 +2314,7 @@ static void test_string_data_process( int i ) ok( len == test_data[i].len, "wrong size %u / %u\n", len, test_data[i].len ); memcpy( bufferW, test_data[i].strW, test_data[i].len ); bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; - ok( !memcmp( data, bufferW, len ), - "wrong data %s\n", wine_dbgstr_wn( data, (len + 1) / sizeof(WCHAR) )); + ok( !memcmp( data, bufferW, len ), "wrong data %s\n", wine_dbgstr_an( data, len )); data = GetClipboardData( CF_TEXT ); if (test_data[i].len >= sizeof(WCHAR)) { @@ -2325,7 +2324,8 @@ static void test_string_data_process( int i ) bufferA, ARRAY_SIZE(bufferA), NULL, NULL ); bufferA[len2 - 1] = 0; ok( len == len2, "wrong size %u / %u\n", len, len2 ); - ok( !memcmp( data, bufferA, len ), "wrong data %.*s\n", len, (char *)data ); + ok( !memcmp( data, bufferA, len ), "wrong data %s, expected %s\n", + wine_dbgstr_an( data, len ), wine_dbgstr_an( bufferA, len2 )); } else { From d4ba7494e5ff4fb2df9de94462b32defb50b9e8e Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 21 Dec 2022 18:31:41 +0100 Subject: [PATCH 043/758] user32: Fix a SetClipboardData() underflow and improve the tests. A 0 or 1 byte Unicode string cannot be NUL terminated. That does stop Windows 10 and older from doing so which is why the 1 byte test must be skipped to avoid a crash in the 64-bit case. But in such cases SetClipboardData() fails on Windows 11 and so should Wine (instead of underflowing the buffer like Windows). Reorganize the test data by increasing size. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54192 (cherry picked from commit ee4f8cbb2066bf7ff2721fb081c1ab091ed8840e) --- dlls/user32/clipboard.c | 1 + dlls/user32/tests/clipboard.c | 73 ++++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/dlls/user32/clipboard.c b/dlls/user32/clipboard.c index dd33bd7df82..f006e205e05 100644 --- a/dlls/user32/clipboard.c +++ b/dlls/user32/clipboard.c @@ -160,6 +160,7 @@ static HANDLE marshal_data( UINT format, HANDLE handle, size_t *ret_size ) WCHAR *ptr; if (!(size = GlobalSize( handle ))) return 0; if ((data_size_t)size != size) return 0; + if (size < sizeof(WCHAR)) return 0; if (!(ptr = GlobalLock( handle ))) return 0; ptr[(size + 1) / sizeof(WCHAR) - 1] = 0; /* enforce null-termination */ GlobalUnlock( handle ); diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index 09f01b7481a..38249c24ea0 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -2217,35 +2217,39 @@ static const struct UINT len; } test_data[] = { - { "foo", {}, 3 }, /* 0 */ + { "foo", {}, 3 }, /* 0 */ { "foo", {}, 4 }, { "foo\0bar", {}, 7 }, { "foo\0bar", {}, 8 }, - { "", {'f','o','o'}, 3 * sizeof(WCHAR) }, - { "", {'f','o','o',0}, 4 * sizeof(WCHAR) }, /* 5 */ + { "", {}, 0 }, + { "", {'f'}, 1 }, /* 5 */ + { "", {'f'}, 2 }, + { "", {'f','o','o'}, 5 }, + { "", {'f','o','o'}, 6 }, + { "", {'f','o','o',0}, 7 }, /* 10 */ + { "", {'f','o','o',0}, 8 }, + { "", {'f','o','o',0,'b'}, 9 }, { "", {'f','o','o',0,'b','a','r'}, 7 * sizeof(WCHAR) }, { "", {'f','o','o',0,'b','a','r',0}, 8 * sizeof(WCHAR) }, - { "", {'f','o','o'}, 1 }, - { "", {'f','o','o'}, 2 }, - { "", {'f','o','o'}, 5 }, /* 10 */ - { "", {'f','o','o',0}, 7 }, - { "", {'f','o','o',0}, 9 }, }; static void test_string_data(void) { UINT i; BOOL r; - HANDLE data; + HANDLE data, clip; char cmd[16]; char bufferA[12]; WCHAR bufferW[12]; for (i = 0; i < ARRAY_SIZE(test_data); i++) { - /* 1-byte Unicode strings crash on Win64 */ #ifdef _WIN64 - if (!test_data[i].strA[0] && test_data[i].len < sizeof(WCHAR)) continue; + /* Before Windows 11 1-byte Unicode strings crash on Win64 + * because it underflows when 'appending' a 2 bytes NUL character! + */ + if (!test_data[i].strA[0] && test_data[i].len < sizeof(WCHAR) && + strcmp(winetest_platform, "windows") == 0) continue; #endif winetest_push_context("%d", i); r = open_clipboard( 0 ); @@ -2265,11 +2269,17 @@ static void test_string_data(void) else { memcpy( data, test_data[i].strW, test_data[i].len ); - SetClipboardData( CF_UNICODETEXT, data ); - memcpy( bufferW, test_data[i].strW, test_data[i].len ); - bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; - ok( !memcmp( data, bufferW, test_data[i].len ), - "wrong data %s\n", wine_dbgstr_an( data, test_data[i].len )); + clip = SetClipboardData( CF_UNICODETEXT, data ); + if (test_data[i].len >= sizeof(WCHAR)) + { + ok( clip == data, "SetClipboardData() returned %p != %p\n", clip, data ); + memcpy( bufferW, test_data[i].strW, test_data[i].len ); + bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; + ok( !memcmp( data, bufferW, test_data[i].len ), + "wrong data %s\n", wine_dbgstr_an( data, test_data[i].len )); + } + else + ok( !clip || broken(clip != NULL), "expected SetClipboardData() to fail\n" ); } r = CloseClipboard(); ok( r, "gle %ld\n", GetLastError() ); @@ -2309,12 +2319,27 @@ static void test_string_data_process( int i ) else { data = GetClipboardData( CF_UNICODETEXT ); - ok( data != 0, "could not get data\n" ); - len = GlobalSize( data ); - ok( len == test_data[i].len, "wrong size %u / %u\n", len, test_data[i].len ); - memcpy( bufferW, test_data[i].strW, test_data[i].len ); - bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; - ok( !memcmp( data, bufferW, len ), "wrong data %s\n", wine_dbgstr_an( data, len )); + if (!data) + { + ok( test_data[i].len < sizeof(WCHAR), "got no data for len=%d\n", test_data[i].len ); + ok( !IsClipboardFormatAvailable( CF_UNICODETEXT ), "unicode available\n" ); + } + else if (test_data[i].len == 0) + { + /* 0-byte string handling is broken on Windows <= 10 */ + len = GlobalSize( data ); + ok( broken(len == 1), "wrong size %u / 0\n", len ); + ok( broken(*((char*)data) == 0), "wrong data %s\n", wine_dbgstr_an( data, len )); + } + else + { + len = GlobalSize( data ); + ok( len == test_data[i].len, "wrong size %u / %u\n", len, test_data[i].len ); + memcpy( bufferW, test_data[i].strW, test_data[i].len ); + bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; + ok( !memcmp( data, bufferW, len ), "wrong data %s\n", wine_dbgstr_an( data, len )); + } + data = GetClipboardData( CF_TEXT ); if (test_data[i].len >= sizeof(WCHAR)) { @@ -2329,8 +2354,10 @@ static void test_string_data_process( int i ) } else { + BOOL available = IsClipboardFormatAvailable( CF_TEXT ); + ok( !available || broken(available /* win10 */), + "available text %d\n", available ); ok( !data, "got data for empty string\n" ); - ok( IsClipboardFormatAvailable( CF_TEXT ), "text not available\n" ); } } r = CloseClipboard(); From 49b44d890c9688ac906d129345f2dbcc07a2e145 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 21 Dec 2022 18:48:29 +0100 Subject: [PATCH 044/758] user32: Fix a SetClipboardData() buffer overflow. Wine would append a correctly aligned NUL Unicode character to terminate the string but overflow the buffer by one byte for odd-sized strings. Windows instead overwrites the last two buffer bytes with a NUL Unicode character which ends up being misaligned for odd-sized strings. The clipboard data has a size field anyway so match the Windows behavior. Tweak the tests to show that SetClipboardData() can overwrite half of the Unicode string's last character. (cherry picked from commit 605ecafa67a4e034328b69396581e2f9b60d7af3) --- dlls/user32/clipboard.c | 5 +++-- dlls/user32/tests/clipboard.c | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/dlls/user32/clipboard.c b/dlls/user32/clipboard.c index f006e205e05..72c7720dc17 100644 --- a/dlls/user32/clipboard.c +++ b/dlls/user32/clipboard.c @@ -157,12 +157,13 @@ static HANDLE marshal_data( UINT format, HANDLE handle, size_t *ret_size ) } case CF_UNICODETEXT: { - WCHAR *ptr; + char *ptr; if (!(size = GlobalSize( handle ))) return 0; if ((data_size_t)size != size) return 0; if (size < sizeof(WCHAR)) return 0; if (!(ptr = GlobalLock( handle ))) return 0; - ptr[(size + 1) / sizeof(WCHAR) - 1] = 0; /* enforce null-termination */ + /* enforce nul-termination the Windows way: ignoring alignment */ + *((WCHAR *)(ptr + size) - 1) = 0; GlobalUnlock( handle ); *ret_size = size; return handle; diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index 38249c24ea0..87df06b6621 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -2224,11 +2224,11 @@ static const struct { "", {}, 0 }, { "", {'f'}, 1 }, /* 5 */ { "", {'f'}, 2 }, - { "", {'f','o','o'}, 5 }, - { "", {'f','o','o'}, 6 }, - { "", {'f','o','o',0}, 7 }, /* 10 */ - { "", {'f','o','o',0}, 8 }, - { "", {'f','o','o',0,'b'}, 9 }, + { "", {0x3b1,0x3b2,0x3b3}, 5 }, /* Alpha, beta, ... */ + { "", {0x3b1,0x3b2,0x3b3}, 6 }, + { "", {0x3b1,0x3b2,0x3b3,0}, 7 }, /* 10 */ + { "", {0x3b1,0x3b2,0x3b3,0}, 8 }, + { "", {0x3b1,0x3b2,0x3b3,0,0x3b4}, 9 }, { "", {'f','o','o',0,'b','a','r'}, 7 * sizeof(WCHAR) }, { "", {'f','o','o',0,'b','a','r',0}, 8 * sizeof(WCHAR) }, }; @@ -2274,7 +2274,7 @@ static void test_string_data(void) { ok( clip == data, "SetClipboardData() returned %p != %p\n", clip, data ); memcpy( bufferW, test_data[i].strW, test_data[i].len ); - bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; + *((WCHAR *)((char *)bufferW + test_data[i].len) - 1) = 0; ok( !memcmp( data, bufferW, test_data[i].len ), "wrong data %s\n", wine_dbgstr_an( data, test_data[i].len )); } @@ -2336,7 +2336,7 @@ static void test_string_data_process( int i ) len = GlobalSize( data ); ok( len == test_data[i].len, "wrong size %u / %u\n", len, test_data[i].len ); memcpy( bufferW, test_data[i].strW, test_data[i].len ); - bufferW[(test_data[i].len + 1) / sizeof(WCHAR) - 1] = 0; + *((WCHAR *)((char *)bufferW + test_data[i].len) - 1) = 0; ok( !memcmp( data, bufferW, len ), "wrong data %s\n", wine_dbgstr_an( data, len )); } From ec5bdfb65c75c7559e44e5482bc93400e347d68a Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:43:08 -0600 Subject: [PATCH 045/758] win32u: Make call_messageAtoW() static. (cherry picked from commit e634f2365946d5c685e1679078e590d94b5e1327) --- dlls/win32u/message.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index fc405f39d4c..62924119c7d 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3099,8 +3099,8 @@ static BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping * * Call a window procedure, translating args from Ansi to Unicode. */ -LRESULT call_messageAtoW( winproc_callback_t callback, HWND hwnd, UINT msg, WPARAM wparam, - LPARAM lparam, LRESULT *result, void *arg, enum wm_char_mapping mapping ) +static LRESULT call_messageAtoW( winproc_callback_t callback, HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam, LRESULT *result, void *arg, enum wm_char_mapping mapping ) { LRESULT ret = 0; From af318efd6c6551098c0e764cfcb23342832e8e69 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:43:45 -0600 Subject: [PATCH 046/758] win32u: Make the global "caret" structure static. (cherry picked from commit 035ac4df7af40282829d8a3be8faf59810328c95) --- dlls/win32u/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 94554fa4c5d..c537e17a157 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2050,7 +2050,7 @@ BOOL set_foreground_window( HWND hwnd, BOOL mouse ) return ret; } -struct +static struct { HBITMAP bitmap; unsigned int timeout; From 461505c65f1fc0766d7981d075f5e9dc3d70fbe6 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:44:36 -0600 Subject: [PATCH 047/758] win32u: Make create_brush() hidden. (cherry picked from commit ffe262b29b4617d24d4562f7efb32ecc39fb9c4a) --- dlls/win32u/ntgdi_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index be3e4d8cd56..53674e0661a 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -152,7 +152,7 @@ extern DWORD stretch_bits( const BITMAPINFO *src_info, struct bitblt_coords *src extern void get_mono_dc_colors( DC *dc, int color_table_size, BITMAPINFO *info, int count ) DECLSPEC_HIDDEN; /* brush.c */ -extern HBRUSH create_brush( const LOGBRUSH *brush ); +extern HBRUSH create_brush( const LOGBRUSH *brush ) DECLSPEC_HIDDEN; extern BOOL store_brush_pattern( LOGBRUSH *brush, struct brush_pattern *pattern ) DECLSPEC_HIDDEN; extern void free_brush_pattern( struct brush_pattern *pattern ) DECLSPEC_HIDDEN; From 1550c206da23e68bbd85e448421927b685213e8a Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:45:08 -0600 Subject: [PATCH 048/758] win32u: Make draw_frame_caption() static. (cherry picked from commit e729c3bb70d4c733dd86a3cbd8176c1fd36e650b) --- dlls/win32u/defwnd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index b28d02a79f4..a7f1a11897a 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -1302,7 +1302,7 @@ static BOOL draw_push_button( HDC dc, RECT *r, UINT flags ) return TRUE; } -BOOL draw_frame_caption( HDC dc, RECT *r, UINT flags ) +static BOOL draw_frame_caption( HDC dc, RECT *r, UINT flags ) { RECT rect; int small_diam = make_square_rect( r, &rect ) - 2; From fc3417601ce6771bbcb8a87d5d0bb52e0593be13 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:46:06 -0600 Subject: [PATCH 049/758] win32u: Make draw_scroll_bar() static. (cherry picked from commit a0a3c25e84a4a5adb689c0b4702d01c1d4ace098) --- dlls/win32u/scroll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c index ce3af5189d3..19a9a1379f4 100644 --- a/dlls/win32u/scroll.c +++ b/dlls/win32u/scroll.c @@ -282,7 +282,7 @@ static BOOL get_scroll_bar_rect( HWND hwnd, int bar, RECT *rect, int *arrow_size * * Redraw the whole scrollbar. */ -void draw_scroll_bar( HWND hwnd, HDC hdc, int bar, enum SCROLL_HITTEST hit_test, +static void draw_scroll_bar( HWND hwnd, HDC hdc, int bar, enum SCROLL_HITTEST hit_test, const struct SCROLL_TRACKING_INFO *tracking_info, BOOL draw_arrows, BOOL draw_interior ) { From 29244e203d808166ac8f63c4e051093c9389d77f Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:47:17 -0600 Subject: [PATCH 050/758] win32u: Make DrawTextW() hidden. (cherry picked from commit 4ccb65e23015f54178ff6f58ac89a7671f28caa3) --- dlls/win32u/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 679da4cc83a..da288010c0b 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -7101,7 +7101,7 @@ BOOL WINAPI NtGdiGetCharWidthInfo( HDC hdc, struct char_width_info *info ) /*********************************************************************** * DrawTextW (win32u.so) */ -INT WINAPI DrawTextW( HDC hdc, const WCHAR *str, INT count, RECT *rect, UINT flags ) +INT WINAPI DECLSPEC_HIDDEN DrawTextW( HDC hdc, const WCHAR *str, INT count, RECT *rect, UINT flags ) { struct draw_text_params *params; ULONG ret_len, size; From 5767370e502eea5e3c9e37a77ab5031feae3c129 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:47:56 -0600 Subject: [PATCH 051/758] win32u: Make get_winproc_ptr() static. (cherry picked from commit 571f33cae67076d44569a94d8849f1c55a624e03) --- dlls/win32u/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/class.c b/dlls/win32u/class.c index 9f3b4251e65..396e2285797 100644 --- a/dlls/win32u/class.c +++ b/dlls/win32u/class.c @@ -108,7 +108,7 @@ static WINDOWPROC *find_winproc( WNDPROC func, BOOL ansi ) /* return the window proc for a given handle, or NULL for an invalid handle, * or WINPROC_PROC16 for a handle to a 16-bit proc. */ -WINDOWPROC *get_winproc_ptr( WNDPROC handle ) +static WINDOWPROC *get_winproc_ptr( WNDPROC handle ) { UINT index = LOWORD(handle); if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return NULL; From f64236757301ebdb1d0be1d61a4fe2302c599da4 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:48:36 -0600 Subject: [PATCH 052/758] win32u: Make ImmProcessKey() hidden. (cherry picked from commit d2e60f28c141640e7b4a681b24b41cbd3c092fec) --- dlls/win32u/imm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index db077dbbef0..c35ca17989e 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -394,7 +394,7 @@ void cleanup_imm_thread(void) NtUserDestroyInputContext( UlongToHandle( thread_info->client_info.default_imc )); } -BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM key_data, DWORD unknown ) +BOOL WINAPI DECLSPEC_HIDDEN ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM key_data, DWORD unknown ) { struct imm_process_key_params params = { .hwnd = hwnd, .hkl = hkl, .vkey = vkey, .key_data = key_data }; From 7ed5ab91e982bb743acabd309213e29356df8752 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:48:46 -0600 Subject: [PATCH 053/758] win32u: Make ImmTranslateMessage() hidden. (cherry picked from commit 646d55adb224a79f8d23802c07b6157592994c46) --- dlls/win32u/imm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index c35ca17989e..d28648520a8 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -403,7 +403,7 @@ BOOL WINAPI DECLSPEC_HIDDEN ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM return KeUserModeCallback( NtUserImmProcessKey, ¶ms, sizeof(params), &ret_ptr, &ret_len ); } -BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM key_data ) +BOOL WINAPI DECLSPEC_HIDDEN ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM key_data ) { struct imm_translate_message_params params = { .hwnd = hwnd, .msg = msg, .wparam = wparam, .key_data = key_data }; From 92362dd6f7c24a893e413fe441c9f32cfb5dee8b Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:49:07 -0600 Subject: [PATCH 054/758] win32u: Make is_child() hidden. (cherry picked from commit 470723d70f88636134855ce586e3886d8f9abb88) --- dlls/win32u/ntuser_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 44af8158c6f..8ab1b35291e 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -273,7 +273,7 @@ static inline UINT win_get_flags( HWND hwnd ) } WND *get_win_ptr( HWND hwnd ) DECLSPEC_HIDDEN; -BOOL is_child( HWND parent, HWND child ); +BOOL is_child( HWND parent, HWND child ) DECLSPEC_HIDDEN; BOOL is_window( HWND hwnd ) DECLSPEC_HIDDEN; #if defined(__i386__) || defined(__x86_64__) From d45ede95d3842ac2ff65e01eb4b8e33297da1d88 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 27 Jan 2023 10:23:56 +0100 Subject: [PATCH 055/758] user32: Copy directly to the buffer in unpack_message(). This avoids compiler warnings. (cherry picked from commit d5756ff8cfb6231cef952f4265bfcf5b2ebb42d5) --- dlls/user32/winproc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/user32/winproc.c b/dlls/user32/winproc.c index 6cd41b51435..9fd35e1d24e 100644 --- a/dlls/user32/winproc.c +++ b/dlls/user32/winproc.c @@ -831,7 +831,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa if (!check_string( str, size )) return FALSE; cs.lpszClass = str; } - memcpy( &ps->cs, &cs, sizeof(cs) ); + memcpy( *buffer, &cs, sizeof(cs) ); break; } case WM_GETTEXT: @@ -865,7 +865,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa dis.hDC = unpack_handle( ps->dis.hDC ); dis.rcItem = ps->dis.rcItem; dis.itemData = (ULONG_PTR)unpack_ptr( ps->dis.itemData ); - memcpy( &ps->dis, &dis, sizeof(dis) ); + memcpy( *buffer, &dis, sizeof(dis) ); break; } case WM_MEASUREITEM: @@ -878,7 +878,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa mis.itemWidth = ps->mis.itemWidth; mis.itemHeight = ps->mis.itemHeight; mis.itemData = (ULONG_PTR)unpack_ptr( ps->mis.itemData ); - memcpy( &ps->mis, &mis, sizeof(mis) ); + memcpy( *buffer, &mis, sizeof(mis) ); break; } case WM_DELETEITEM: @@ -890,7 +890,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa dls.itemID = ps->dls.itemID; dls.hwndItem = unpack_handle( ps->dls.hwndItem ); dls.itemData = (ULONG_PTR)unpack_ptr( ps->dls.itemData ); - memcpy( &ps->dls, &dls, sizeof(dls) ); + memcpy( *buffer, &dls, sizeof(dls) ); break; } case WM_COMPAREITEM: @@ -905,7 +905,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa cis.itemID2 = ps->cis.itemID2; cis.itemData2 = (ULONG_PTR)unpack_ptr( ps->cis.itemData2 ); cis.dwLocaleId = ps->cis.dwLocaleId; - memcpy( &ps->cis, &cis, sizeof(cis) ); + memcpy( *buffer, &cis, sizeof(cis) ); break; } case WM_WINDOWPOSCHANGING: @@ -920,7 +920,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa wp.cx = ps->wp.cx; wp.cy = ps->wp.cy; wp.flags = ps->wp.flags; - memcpy( &ps->wp, &wp, sizeof(wp) ); + memcpy( *buffer, &wp, sizeof(wp) ); break; } case WM_COPYDATA: @@ -1080,7 +1080,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa mnm.hmenuIn = unpack_handle( ps->mnm.hmenuIn ); mnm.hmenuNext = unpack_handle( ps->mnm.hmenuNext ); mnm.hwndNext = unpack_handle( ps->mnm.hwndNext ); - memcpy( &ps->mnm, &mnm, sizeof(mnm) ); + memcpy( *buffer, &mnm, sizeof(mnm) ); break; } case WM_SIZING: @@ -1116,7 +1116,7 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa if (!check_string( str, size )) return FALSE; mcs.szTitle = str; } - memcpy( &ps->mcs, &mcs, sizeof(mcs) ); + memcpy( *buffer, &mcs, sizeof(mcs) ); break; } case WM_MDIGETACTIVE: From 20278b98d6d88f3b1ef78b88a441fb72c4d6ad85 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:50:38 -0600 Subject: [PATCH 056/758] win32u: Make send_message_timeout() hidden. (cherry picked from commit b2dd38372f9b800fd54704546c8a88c22eb9013f) --- dlls/win32u/win32u_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 87c76e7422c..bba36a43ae0 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -316,7 +316,7 @@ extern LRESULT send_internal_message_timeout( DWORD dest_pid, DWORD dest_tid, UI extern LRESULT send_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) DECLSPEC_HIDDEN; extern BOOL send_notify_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi ) DECLSPEC_HIDDEN; extern LRESULT send_message_timeout( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, - UINT flags, UINT timeout, BOOL ansi ); + UINT flags, UINT timeout, BOOL ansi ) DECLSPEC_HIDDEN; /* rawinput.c */ extern BOOL process_rawinput_message( MSG *msg, UINT hw_id, const struct hardware_msg_data *msg_data ) DECLSPEC_HIDDEN; From 4ba8bb29dc45f95b6fb223ed3060b97de5d266f3 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:53:09 -0600 Subject: [PATCH 057/758] win32u: Make set_visible_region() hidden. (cherry picked from commit dd6d837b62f5e0e3a2b1f0157c4a41b59d4ea991) --- dlls/win32u/ntgdi_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index 53674e0661a..ce5e30ab07d 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -160,7 +160,7 @@ extern void free_brush_pattern( struct brush_pattern *pattern ) DECLSPEC_HIDDEN; extern BOOL clip_device_rect( DC *dc, RECT *dst, const RECT *src ) DECLSPEC_HIDDEN; extern BOOL clip_visrect( DC *dc, RECT *dst, const RECT *src ) DECLSPEC_HIDDEN; extern void set_visible_region( HDC hdc, HRGN hrgn, const RECT *vis_rect, - const RECT *device_rect, struct window_surface *surface ); + const RECT *device_rect, struct window_surface *surface ) DECLSPEC_HIDDEN; extern void update_dc_clipping( DC * dc ) DECLSPEC_HIDDEN; /* Return the total DC region (if any) */ From fdb944388529ab35a0f01131085cea6cd10733c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:29:37 +0200 Subject: [PATCH 058/758] winex11: Make client_foreign_window_proc hidden. (cherry picked from commit 2b0677341704affce04710a633ba9fb5b3a134d1) --- dlls/winex11.drv/x11drv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 07d821e76a8..e4fe442ccbc 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -459,7 +459,7 @@ extern int xrender_error_base DECLSPEC_HIDDEN; extern int xfixes_event_base DECLSPEC_HIDDEN; extern char *process_name DECLSPEC_HIDDEN; extern Display *clipboard_display DECLSPEC_HIDDEN; -extern WNDPROC client_foreign_window_proc; +extern WNDPROC client_foreign_window_proc DECLSPEC_HIDDEN; extern HANDLE steam_overlay_event DECLSPEC_HIDDEN; extern HANDLE steam_keyboard_event DECLSPEC_HIDDEN; From 6110a8607a565338e7eb224ce790e6f35765d8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:30:00 +0200 Subject: [PATCH 059/758] winex11: Include x11drv.h in xrandr.c even if compiling without xrandr. Make sure we pull in the definition of X11DRV_XRandR_Init(), in particular because it has DECLSPEC_HIDDEN. (cherry picked from commit c05f5445dc2c330539cfb22fb5b27506061d26b4) --- dlls/winex11.drv/xrandr.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c index 35998877520..4186b404e6b 100644 --- a/dlls/winex11.drv/xrandr.c +++ b/dlls/winex11.drv/xrandr.c @@ -28,19 +28,17 @@ #define NONAMELESSSTRUCT #define NONAMELESSUNION - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(xrandr); - -#ifdef SONAME_LIBXRANDR - #include #include #include #include #include #include "x11drv.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(xrandr); + +#ifdef SONAME_LIBXRANDR #define VK_NO_PROTOTYPES #define WINE_VK_HOST From 836706e49a15aa6e787e40d22e21d5db5fedabe4 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Sat, 11 Feb 2023 12:52:28 -0700 Subject: [PATCH 060/758] win32u: Avoid calling RtlInitUnicodeString on a static constant. (cherry picked from commit 33ddf019f7cb13f05aaeec984f3b9f3ca3411da9) --- dlls/win32u/imm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index d28648520a8..1ccc09c97b2 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -277,12 +277,11 @@ BOOL register_imm_window( HWND hwnd ) /* Create default IME window */ if (!thread_data->window_cnt++) { - UNICODE_STRING class_name, name; static const WCHAR imeW[] = {'I','M','E',0}; static const WCHAR default_imeW[] = {'D','e','f','a','u','l','t',' ','I','M','E',0}; + UNICODE_STRING class_name = RTL_CONSTANT_STRING( imeW ); + UNICODE_STRING name = RTL_CONSTANT_STRING( default_imeW ); - RtlInitUnicodeString( &class_name, imeW ); - RtlInitUnicodeString( &name, default_imeW ); thread_data->default_hwnd = NtUserCreateWindowEx( 0, &class_name, &class_name, &name, WS_POPUP | WS_DISABLED | WS_CLIPSIBLINGS, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, FALSE ); From 69388db7592a47b5011aceaf6f0ca0a93510ed3b Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Mon, 13 Feb 2023 18:34:44 +0100 Subject: [PATCH 061/758] winex11.drv: Fix a typo in a comment. (cherry picked from commit 9338531ee40785f2237414205f8ba050e640acc1) --- dlls/winex11.drv/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 36bc1d58302..abb1e141b3d 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1196,7 +1196,7 @@ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) if (!(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN)) || is_virtual_desktop()) return; - /* If the current display device handler can not detect dynamic device changes, do not use + /* If the current display device handler cannot detect dynamic device changes, do not use * _NET_WM_FULLSCREEN_MONITORS because xinerama_get_fullscreen_monitors() may report wrong * indices because of stale xinerama monitor information */ if (!X11DRV_DisplayDevices_SupportEventHandlers()) From c7e5849a949ed1a6999752788535f131ada69d8b Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 28 Feb 2023 09:49:36 -0700 Subject: [PATCH 062/758] winex11: Use RTL_CONSTANT_STRING instead of reimplementing it. (cherry picked from commit 129d861656b61f9b308e9fb5f563af735871b533) --- dlls/winex11.drv/desktop.c | 4 ++-- dlls/winex11.drv/window.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index f7f49f6ca4e..b7896632ca9 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -227,8 +227,8 @@ static LONG X11DRV_desktop_set_current_mode( ULONG_PTR id, const DEVMODEW *mode static void query_desktop_work_area( RECT *rc_work ) { - static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d'}; - UNICODE_STRING str = { sizeof(trayW), sizeof(trayW), (WCHAR *)trayW }; + static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; + UNICODE_STRING str = RTL_CONSTANT_STRING( trayW ); RECT rect; HWND hwnd = NtUserFindWindowEx( 0, 0, &str, NULL, 0 ); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index abb1e141b3d..43fd93536cc 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3359,8 +3359,8 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags, /* check if the window icon should be hidden (i.e. moved off-screen) */ static BOOL hide_icon( struct x11drv_win_data *data ) { - static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d'}; - UNICODE_STRING str = { sizeof(trayW), sizeof(trayW), (WCHAR *)trayW }; + static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; + UNICODE_STRING str = RTL_CONSTANT_STRING( trayW ); if (data->managed) return TRUE; /* hide icons in desktop mode when the taskbar is active */ From bf3d72a43aa09cd755cc88df90d6654740e9aba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Feb 2023 10:57:58 +0100 Subject: [PATCH 063/758] winex11: Assume that Xkb extension is available. (cherry picked from commit ad5cb8305f4ebc10992113f8f6a724d5f33a6bf8) --- configure.ac | 7 ------- dlls/winex11.drv/keyboard.c | 37 +++++++++------------------------- dlls/winex11.drv/x11drv.h | 1 - dlls/winex11.drv/x11drv_main.c | 14 +++---------- 4 files changed, 13 insertions(+), 46 deletions(-) diff --git a/configure.ac b/configure.ac index 5d3238618ea..e100509c3f7 100644 --- a/configure.ac +++ b/configure.ac @@ -1195,13 +1195,6 @@ then # include #endif]) - dnl *** Check for X keyboard extension - if test "$ac_cv_header_X11_XKBlib_h" = "yes" - then - AC_CHECK_LIB(X11, XkbQueryExtension, - AC_DEFINE(HAVE_XKB, 1, [Define if you have the XKB extension]),,[$X_LIBS $X_EXTRA_LIBS]) - fi - dnl *** Check for X cursor if test "$ac_cv_header_X11_Xcursor_Xcursor_h" = "yes" then diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 12bb2b863cb..caf1dbc6b56 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -34,9 +34,7 @@ #include #include #include -#ifdef HAVE_X11_XKBLIB_H #include -#endif #include #include @@ -66,7 +64,6 @@ WINE_DECLARE_DEBUG_CHANNEL(key); static const unsigned int ControlMask = 1 << 2; static int min_keycode, max_keycode, keysyms_per_keycode; -static KeySym *key_mapping; static WORD keyc2vkey[256], keyc2scan[256]; static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */ @@ -1089,14 +1086,6 @@ static const WORD xfree86_vendor_key_vkey[256] = 0, 0, 0, 0, 0, 0, 0, 0 /* 1008FFF8 */ }; -static inline KeySym keycode_to_keysym( Display *display, KeyCode keycode, int index ) -{ -#ifdef HAVE_XKB - if (use_xkb) return XkbKeycodeToKeysym(display, keycode, 0, index); -#endif - return key_mapping[(keycode - min_keycode) * keysyms_per_keycode + index]; -} - /* Returns the Windows virtual key code associated with the X event */ /* kbd_section must be held */ static WORD EVENT_event_to_vkey( XIC xic, XKeyEvent *e) @@ -1443,13 +1432,11 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) for (keyc = min_keycode; keyc <= max_keycode; keyc++) { /* get data for keycode from X server */ for (i = 0; i < syms; i++) { - if (!(keysym = keycode_to_keysym (display, keyc, i))) continue; + if (!(keysym = XkbKeycodeToKeysym( display, keyc, 0, i ))) continue; /* Allow both one-byte and two-byte national keysyms */ if ((keysym < 0x8000) && (keysym != ' ')) { -#ifdef HAVE_XKB - if (!use_xkb || !XkbTranslateKeySym(display, &keysym, 0, &ckey[keyc][i], 1, NULL)) -#endif + if (!XkbTranslateKeySym(display, &keysym, 0, &ckey[keyc][i], 1, NULL)) { TRACE("XKB could not translate keysym %04lx\n", keysym); /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent @@ -1598,9 +1585,7 @@ void X11DRV_InitKeyboard( Display *display ) pthread_mutex_lock( &kbd_mutex ); XDisplayKeycodes(display, &min_keycode, &max_keycode); - if (key_mapping) XFree( key_mapping ); - key_mapping = XGetKeyboardMapping(display, min_keycode, - max_keycode + 1 - min_keycode, &keysyms_per_keycode); + XFree( XGetKeyboardMapping( display, min_keycode, max_keycode + 1 - min_keycode, &keysyms_per_keycode ) ); mmp = XGetModifierMapping(display); kcp = mmp->modifiermap; @@ -1614,12 +1599,12 @@ void X11DRV_InitKeyboard( Display *display ) int k; for (k = 0; k < keysyms_per_keycode; k += 1) - if (keycode_to_keysym(display, *kcp, k) == XK_Num_Lock) + if (XkbKeycodeToKeysym( display, *kcp, 0, k ) == XK_Num_Lock) { NumLockMask = 1 << i; TRACE_(key)("NumLockMask is %x\n", NumLockMask); } - else if (keycode_to_keysym(display, *kcp, k) == XK_Scroll_Lock) + else if (XkbKeycodeToKeysym( display, *kcp, 0, k ) == XK_Scroll_Lock) { ScrollLockMask = 1 << i; TRACE_(key)("ScrollLockMask is %x\n", ScrollLockMask); @@ -1671,12 +1656,10 @@ void X11DRV_InitKeyboard( Display *display ) /* we seem to need to search the layout-dependent scancodes */ int maxlen=0,maxval=-1,ok; for (i=0; i #include #include -#ifdef HAVE_XKB #include -#endif #ifdef HAVE_X11_EXTENSIONS_XRENDER_H #include #endif @@ -74,7 +72,6 @@ BOOL usexvidmode = FALSE; BOOL usexrandr = TRUE; BOOL usexcomposite = TRUE; BOOL use_xfixes = FALSE; -BOOL use_xkb = TRUE; BOOL use_take_focus = FALSE; BOOL use_primary_selection = FALSE; BOOL use_system_cursors = TRUE; @@ -828,9 +825,7 @@ static NTSTATUS x11drv_init( void *arg ) #endif X11DRV_XInput2_Load(); -#ifdef HAVE_XKB - if (use_xkb) use_xkb = XkbUseExtension( gdi_display, NULL, NULL ); -#endif + XkbUseExtension( gdi_display, NULL, NULL ); X11DRV_InitKeyboard( gdi_display ); X11DRV_InitMouse( gdi_display ); if (use_xim) use_xim = X11DRV_InitXIM( input_style ); @@ -941,11 +936,8 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */ -#ifdef HAVE_XKB - if (use_xkb && XkbUseExtension( data->display, NULL, NULL )) - XkbSetDetectableAutoRepeat( data->display, True, NULL ); -#endif - + XkbUseExtension( data->display, NULL, NULL ); + XkbSetDetectableAutoRepeat( data->display, True, NULL ); if (TRACE_ON(synchronous)) XSynchronize( data->display, True ); set_queue_display_fd( data->display ); From f220fae08f657d2ba54b07f23f04b9bd95bec367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 10:04:07 +0100 Subject: [PATCH 064/758] user32/tests: Test VK_MENU effect on ToUnicode. (cherry picked from commit 3785dd1f37534777a0e20f162aee3d5d2e82eb3a) --- dlls/user32/tests/input.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index f8b40099091..1a9757bd1fb 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3042,6 +3042,7 @@ static void test_key_map(void) #define shift 1 #define ctrl 2 +#define menu 4 static const struct tounicode_tests { @@ -3054,6 +3055,9 @@ static const struct tounicode_tests { { 0, 0, 'a', 1, {'a',0}}, { 0, shift, 'a', 1, {'A',0}}, + { 0, menu, 'a', 1, {'a',0}}, + { 0, shift|menu, 'a', 1, {'A',0}}, + { 0, shift|ctrl|menu, 'a', 0, {}}, { 0, ctrl, 'a', 1, {1, 0}}, { 0, shift|ctrl, 'a', 1, {1, 0}}, { VK_TAB, ctrl, 0, 0, {}}, @@ -3142,6 +3146,7 @@ static void test_ToUnicode(void) state[VK_SHIFT] = state[VK_LSHIFT] = (mod & shift) ? HIGHEST_BIT : 0; state[VK_CONTROL] = state[VK_LCONTROL] = (mod & ctrl) ? HIGHEST_BIT : 0; + state[VK_MENU] = state[VK_LMENU] = (mod & menu) ? HIGHEST_BIT : 0; ret = ToUnicode(vk, scan, state, wStr, 4, 0); ok(ret == utests[i].expect_ret, "%d: got %d expected %d\n", i, ret, utests[i].expect_ret); From 7dc9462acd72e60fb864a55c6910401f594aad0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 4 Dec 2022 14:34:29 +0100 Subject: [PATCH 065/758] include: Allow overriding LANGID in module VERSIONINFO. (cherry picked from commit 653321a2b4ca672a25d5cd1a4a5ffb7d58f27f42) --- include/wine/wine_common_ver.rc | 12 ++++++++---- po/ar.po | 4 ++-- po/ast.po | 4 ++-- po/bg.po | 4 ++-- po/ca.po | 4 ++-- po/cs.po | 4 ++-- po/da.po | 4 ++-- po/de.po | 4 ++-- po/el.po | 4 ++-- po/en.po | 4 ++-- po/en_US.po | 4 ++-- po/eo.po | 4 ++-- po/es.po | 4 ++-- po/fa.po | 4 ++-- po/fi.po | 4 ++-- po/fr.po | 4 ++-- po/he.po | 4 ++-- po/hi.po | 4 ++-- po/hr.po | 4 ++-- po/hu.po | 4 ++-- po/it.po | 4 ++-- po/ja.po | 4 ++-- po/ko.po | 4 ++-- po/lt.po | 4 ++-- po/ml.po | 4 ++-- po/nb_NO.po | 4 ++-- po/nl.po | 4 ++-- po/or.po | 4 ++-- po/pa.po | 4 ++-- po/pl.po | 4 ++-- po/pt_BR.po | 4 ++-- po/pt_PT.po | 4 ++-- po/rm.po | 4 ++-- po/ro.po | 4 ++-- po/ru.po | 4 ++-- po/si.po | 4 ++-- po/sk.po | 4 ++-- po/sl.po | 4 ++-- po/sr_RS@cyrillic.po | 4 ++-- po/sr_RS@latin.po | 4 ++-- po/sv.po | 4 ++-- po/ta.po | 4 ++-- po/te.po | 4 ++-- po/th.po | 4 ++-- po/tr.po | 4 ++-- po/uk.po | 4 ++-- po/wa.po | 4 ++-- po/wine.pot | 4 ++-- po/zh_CN.po | 4 ++-- po/zh_TW.po | 4 ++-- 50 files changed, 106 insertions(+), 102 deletions(-) diff --git a/include/wine/wine_common_ver.rc b/include/wine/wine_common_ver.rc index 95ab04666e8..d79062416ed 100644 --- a/include/wine/wine_common_ver.rc +++ b/include/wine/wine_common_ver.rc @@ -115,6 +115,12 @@ never complain. #define WINE_CODEPAGE_HEX WINE_VER_HEXPREFIX(WINE_CODEPAGE) #endif +#ifndef WINE_LANGID +#define WINE_LANGID 0409 /* LANG_ENGLISH/SUBLANG_DEFAULT */ +#endif +#define WINE_LANGID_STR WINE_VER_STRINGIZE(WINE_LANGID) +#define WINE_LANGID_HEX WINE_VER_HEXPREFIX(WINE_LANGID) + VS_VERSION_INFO VERSIONINFO FILEVERSION WINE_FILEVERSION PRODUCTVERSION WINE_PRODUCTVERSION @@ -126,8 +132,7 @@ FILESUBTYPE WINE_FILESUBTYPE { BLOCK "StringFileInfo" { - /* LANG_ENGLISH/SUBLANG_DEFAULT, WINE_CODEPAGE */ - BLOCK "0409" WINE_CODEPAGE_STR + BLOCK WINE_LANGID_STR WINE_CODEPAGE_STR { VALUE "CompanyName", "Microsoft Corporation" /* GameGuard depends on this */ VALUE "FileDescription", WINE_FILEDESCRIPTION_STR @@ -142,7 +147,6 @@ FILESUBTYPE WINE_FILESUBTYPE } BLOCK "VarFileInfo" { - /* LANG_ENGLISH/SUBLANG_DEFAULT, WINE_CODEPAGE */ - VALUE "Translation", 0x0409, WINE_CODEPAGE_HEX + VALUE "Translation", WINE_LANGID_HEX, WINE_CODEPAGE_HEX } } diff --git a/po/ar.po b/po/ar.po index a17773e2606..5fdad68ed12 100644 --- a/po/ar.po +++ b/po/ar.po @@ -4129,11 +4129,11 @@ msgstr "'[object]' ليس عنصر تاريخ" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "واين" diff --git a/po/ast.po b/po/ast.po index 1e0b2398d23..359de815a1a 100644 --- a/po/ast.po +++ b/po/ast.po @@ -3996,11 +3996,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/bg.po b/po/bg.po index 45450a3c193..cac408aef44 100644 --- a/po/bg.po +++ b/po/bg.po @@ -4125,11 +4125,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 #, fuzzy msgid "Wine" diff --git a/po/ca.po b/po/ca.po index ee2bcf7b0a0..00cb01b7ce2 100644 --- a/po/ca.po +++ b/po/ca.po @@ -4101,11 +4101,11 @@ msgstr "'this' no és un objecte de |" msgid "Property cannot have both accessors and a value" msgstr "La propietat no pot tenir ambdós mètodes d'accés i un valor" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "DLL de nucli del Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/cs.po b/po/cs.po index 2c4fd0fc2c2..b2c53f1d99a 100644 --- a/po/cs.po +++ b/po/cs.po @@ -4070,11 +4070,11 @@ msgstr "„%s“ není platný název portu" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/da.po b/po/da.po index 3180d23849d..53aa5e9ac72 100644 --- a/po/da.po +++ b/po/da.po @@ -4166,11 +4166,11 @@ msgstr "«[objekt]» er ikke et dato objekt" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/de.po b/po/de.po index 5edd48f2dc2..e970e9175df 100644 --- a/po/de.po +++ b/po/de.po @@ -4089,11 +4089,11 @@ msgstr "'this' ist kein |-Objekt" msgid "Property cannot have both accessors and a value" msgstr "Eigenschaft kann nicht sowohl Accessoren als auch einen Wert haben" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine-Kernel-DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/el.po b/po/el.po index 9b5f42cae51..f4ca06d96fb 100644 --- a/po/el.po +++ b/po/el.po @@ -4025,11 +4025,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/en.po b/po/en.po index 91419affefd..a5425b52486 100644 --- a/po/en.po +++ b/po/en.po @@ -4076,11 +4076,11 @@ msgstr "'this' is not a | object" msgid "Property cannot have both accessors and a value" msgstr "Property cannot have both accessors and a value" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine kernel DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/en_US.po b/po/en_US.po index 34daca1a1bc..0735be69271 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -4076,11 +4076,11 @@ msgstr "'this' is not a | object" msgid "Property cannot have both accessors and a value" msgstr "Property cannot have both accessors and a value" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine kernel DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/eo.po b/po/eo.po index 17d3c9647e4..f2ff3e010a1 100644 --- a/po/eo.po +++ b/po/eo.po @@ -4034,11 +4034,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/es.po b/po/es.po index cde1ed48458..4f363add9c4 100644 --- a/po/es.po +++ b/po/es.po @@ -4106,11 +4106,11 @@ msgstr "'[this]' no es un objeto Map" msgid "Property cannot have both accessors and a value" msgstr "La propiedad no puede tener tanto descriptores de acceso como un valor" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "DLL de núcle Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/fa.po b/po/fa.po index b9531707e71..2ea2a88163a 100644 --- a/po/fa.po +++ b/po/fa.po @@ -4052,11 +4052,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/fi.po b/po/fi.po index d22ab0b6029..dc90507d60e 100644 --- a/po/fi.po +++ b/po/fi.po @@ -4073,11 +4073,11 @@ msgstr "'this' ei ole |-objekti" msgid "Property cannot have both accessors and a value" msgstr "Ominaisuudella ei voi olla sekä hakufunktiota että arvoa" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Winen ydin-DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/fr.po b/po/fr.po index 8bb0ba73b07..b00b7235c3e 100644 --- a/po/fr.po +++ b/po/fr.po @@ -4098,11 +4098,11 @@ msgstr "« this » n'est pas un objet de type Map" msgid "Property cannot have both accessors and a value" msgstr "La propriété ne peut à la fois avoir une valeur et des accesseurs" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "DLL noyau de Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/he.po b/po/he.po index 26d5a2dbb7b..58dcefcebd4 100644 --- a/po/he.po +++ b/po/he.po @@ -4120,11 +4120,11 @@ msgstr "'%s' אינו שם תקני לפתחה" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/hi.po b/po/hi.po index 2bd8f692cc7..7663549c7a2 100644 --- a/po/hi.po +++ b/po/hi.po @@ -3981,11 +3981,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/hr.po b/po/hr.po index a801b3f74d5..f04c70940fa 100644 --- a/po/hr.po +++ b/po/hr.po @@ -4134,11 +4134,11 @@ msgstr "'[object]' nije vremenski objekt" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/hu.po b/po/hu.po index 88880d8a997..24c0eeecd35 100644 --- a/po/hu.po +++ b/po/hu.po @@ -4184,11 +4184,11 @@ msgstr "'Az [object]' nem egy date (dátum) objektum" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine súgó" diff --git a/po/it.po b/po/it.po index ec6b6b19192..0449017ac23 100644 --- a/po/it.po +++ b/po/it.po @@ -4192,11 +4192,11 @@ msgstr "'[oggetto]' non è un oggetto data" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/ja.po b/po/ja.po index 49f30f5718e..43bf855ddcd 100644 --- a/po/ja.po +++ b/po/ja.po @@ -4072,11 +4072,11 @@ msgstr "'this' は | オブジェクトではありません" msgid "Property cannot have both accessors and a value" msgstr "プロパティはアクセサーと値の両方になることはできません" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/ko.po b/po/ko.po index e5038887f90..c1272917c81 100644 --- a/po/ko.po +++ b/po/ko.po @@ -4061,11 +4061,11 @@ msgstr "'this'는 '|' 개체가 아닙니다" msgid "Property cannot have both accessors and a value" msgstr "속성에 접근자와 값을 둘 다 지정할 수는 없습니다" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine 커널 DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/lt.po b/po/lt.po index 217ba5c9628..e4f347061d6 100644 --- a/po/lt.po +++ b/po/lt.po @@ -4083,11 +4083,11 @@ msgstr "„Šis“ nėra | objektas" msgid "Property cannot have both accessors and a value" msgstr "Savybė negali turėti ir kreipiklių, ir reikšmės" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine branduolio DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/ml.po b/po/ml.po index 7a8d207ea34..b612d8ef96e 100644 --- a/po/ml.po +++ b/po/ml.po @@ -3983,11 +3983,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/nb_NO.po b/po/nb_NO.po index 818e6058644..da72e8d86f8 100644 --- a/po/nb_NO.po +++ b/po/nb_NO.po @@ -4104,11 +4104,11 @@ msgstr "'[object]' er ikke et dataobjekt" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine kjerne-DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/nl.po b/po/nl.po index b6c00c155c2..b6df43c1006 100644 --- a/po/nl.po +++ b/po/nl.po @@ -4093,11 +4093,11 @@ msgstr "'this' is geen | object" msgid "Property cannot have both accessors and a value" msgstr "Eigenschap kan niet zowel accessors als een waarde hebben" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine kernel DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/or.po b/po/or.po index 59e24a848ef..f380aee4c32 100644 --- a/po/or.po +++ b/po/or.po @@ -3981,11 +3981,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/pa.po b/po/pa.po index 46068d3822f..02417daffe3 100644 --- a/po/pa.po +++ b/po/pa.po @@ -3981,11 +3981,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/pl.po b/po/pl.po index 49346b2194c..9689ee339e0 100644 --- a/po/pl.po +++ b/po/pl.po @@ -4105,11 +4105,11 @@ msgstr "'this' nie jest obiektem Map" msgid "Property cannot have both accessors and a value" msgstr "Własność nie może mieć zarówno akcesorów i wartości" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "DLL jądra Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/pt_BR.po b/po/pt_BR.po index dc6fbe28027..15079a024e1 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -4101,11 +4101,11 @@ msgstr "'this' não é um objeto Map" msgid "Property cannot have both accessors and a value" msgstr "Propriedade não pode ter ambos acessores e valor" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Biblioteca de kernel Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/pt_PT.po b/po/pt_PT.po index 3ec661f62ee..55807dedfdd 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -4152,11 +4152,11 @@ msgstr "'[object]' não é um objecto de data" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/rm.po b/po/rm.po index 2573d02eab9..eb130bb02c2 100644 --- a/po/rm.po +++ b/po/rm.po @@ -4011,11 +4011,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 #, fuzzy msgid "Wine" diff --git a/po/ro.po b/po/ro.po index dd8b5357230..d042fde175b 100644 --- a/po/ro.po +++ b/po/ro.po @@ -4107,11 +4107,11 @@ msgstr "„[obiect]” nu este un obiect de tip dată" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/ru.po b/po/ru.po index 1d567cabdcb..483e925f005 100644 --- a/po/ru.po +++ b/po/ru.po @@ -4108,11 +4108,11 @@ msgstr "«this» не объект типа «Map»" msgid "Property cannot have both accessors and a value" msgstr "Свойство не может одновременно иметь методы для доступа и значение" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Библиотека ядра Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/si.po b/po/si.po index 444e97e402b..395e7c3aa23 100644 --- a/po/si.po +++ b/po/si.po @@ -4037,11 +4037,11 @@ msgstr "'%s' වලංගු තොට නමක් නෙමෙයි." msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine කර්නලයේ DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/sk.po b/po/sk.po index 35a24b1ac2b..5a74fd54a99 100644 --- a/po/sk.po +++ b/po/sk.po @@ -4072,11 +4072,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/sl.po b/po/sl.po index 45f5433f134..f2a06472681 100644 --- a/po/sl.po +++ b/po/sl.po @@ -4186,11 +4186,11 @@ msgstr "'[object]' ni predmet datuma" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/sr_RS@cyrillic.po b/po/sr_RS@cyrillic.po index a5815e8aab0..3f6bf0232b1 100644 --- a/po/sr_RS@cyrillic.po +++ b/po/sr_RS@cyrillic.po @@ -4161,11 +4161,11 @@ msgstr "„[object]“ није временски објекат" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/sr_RS@latin.po b/po/sr_RS@latin.po index e446f271429..ea6b3aecae5 100644 --- a/po/sr_RS@latin.po +++ b/po/sr_RS@latin.po @@ -4247,11 +4247,11 @@ msgstr "„[object]“ nije vremenski objekat" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/sv.po b/po/sv.po index 21883ad555f..c4ed81856f5 100644 --- a/po/sv.po +++ b/po/sv.po @@ -4131,11 +4131,11 @@ msgstr "'[object]' är inte ett datumobjekt" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine-kärn-DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/ta.po b/po/ta.po index 50289ad45c3..656f7a07b3a 100644 --- a/po/ta.po +++ b/po/ta.po @@ -3948,11 +3948,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/te.po b/po/te.po index 7dafde4a546..8b69b08e201 100644 --- a/po/te.po +++ b/po/te.po @@ -3981,11 +3981,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/th.po b/po/th.po index 29af5b4a434..41de65e1d68 100644 --- a/po/th.po +++ b/po/th.po @@ -4047,11 +4047,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/tr.po b/po/tr.po index d2ecc1b0fc2..0a6e29835f6 100644 --- a/po/tr.po +++ b/po/tr.po @@ -4099,11 +4099,11 @@ msgstr "'[object]' bir tarih nesnesi değil" msgid "Property cannot have both accessors and a value" msgstr "Nesnenin erişimcisi ve değeri birden olamaz" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine çekirdek DLL'si" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/uk.po b/po/uk.po index cf0ceb06649..57307a18199 100644 --- a/po/uk.po +++ b/po/uk.po @@ -4101,11 +4101,11 @@ msgstr "'це' не є Map об'єкта" msgid "Property cannot have both accessors and a value" msgstr "Властивість не може одночасно мати доступ і значення" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Бібліотека ядра Wine" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/wa.po b/po/wa.po index fcbcf228e00..891cf5fb6e3 100644 --- a/po/wa.po +++ b/po/wa.po @@ -4048,11 +4048,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 #, fuzzy msgid "Wine" diff --git a/po/wine.pot b/po/wine.pot index 21654586e88..5241318e3bf 100644 --- a/po/wine.pot +++ b/po/wine.pot @@ -3935,11 +3935,11 @@ msgstr "" msgid "Property cannot have both accessors and a value" msgstr "" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 6f1772bd3c4..022dd3369a4 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -4026,11 +4026,11 @@ msgstr "'this' 不是 | 对象" msgid "Property cannot have both accessors and a value" msgstr "属性不能同时包含存取器和值" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine kernel DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" diff --git a/po/zh_TW.po b/po/zh_TW.po index f29c109df08..3d888ac2790 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -4034,11 +4034,11 @@ msgstr "'this' 不是一個 | 物件" msgid "Property cannot have both accessors and a value" msgstr "屬性不可同時有存取子和值" -#: include/wine/wine_common_ver.rc:133 +#: include/wine/wine_common_ver.rc:138 msgid "Wine kernel DLL" msgstr "Wine 核心 DLL" -#: include/wine/wine_common_ver.rc:138 dlls/winemac.drv/winemac.rc:32 +#: include/wine/wine_common_ver.rc:143 dlls/winemac.drv/winemac.rc:32 #: programs/wineboot/wineboot.rc:42 programs/winecfg/winecfg.rc:137 msgid "Wine" msgstr "Wine" From 4395e1eae212990853e42568dfce2ff7a7a43ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 08:54:44 +0100 Subject: [PATCH 066/758] oleaut32: Allocate a full pointer when unmarshalling byref arrays. Instead of the 4 bytes array wire size returned by get_type_size, which will truncate the pointer on 64-bit. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54562 (cherry picked from commit 76e7f030feb3b26ae409c2f1027f16168bcdd420) --- dlls/oleaut32/usrmarshal.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/oleaut32/usrmarshal.c b/dlls/oleaut32/usrmarshal.c index aa54a2b092b..4a874a23413 100644 --- a/dlls/oleaut32/usrmarshal.c +++ b/dlls/oleaut32/usrmarshal.c @@ -506,7 +506,10 @@ unsigned char * WINAPI VARIANT_UserUnmarshal(ULONG *pFlags, unsigned char *Buffe ULONG mem_size; Pos += 4; - switch (header->vt & ~VT_BYREF) + /* byref array needs to allocate a SAFEARRAY pointer */ + if (header->vt & VT_ARRAY) + mem_size = sizeof(void *); + else switch (header->vt & ~VT_BYREF) { /* these types have a different memory size compared to wire size */ case VT_UNKNOWN: From fffa2b65a7c3d9055e82dce9a32cec57ff0a09e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 09:59:51 +0100 Subject: [PATCH 067/758] win32u: Map VK_MENU / KBDALT in kbdus_tables pCharModifiers. (cherry picked from commit 2a6b80b508c6fd983729835a3d5c10f87a49f55b) --- dlls/win32u/input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index c537e17a157..9f17b71e0fe 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -135,8 +135,8 @@ static const VK_TO_BIT vk_to_bit[] = static const MODIFIERS modifiers = { .pVkToBit = (VK_TO_BIT *)vk_to_bit, - .wMaxModBits = 3, - .ModNumber = {0, 1, 2, 3}, + .wMaxModBits = 7, + .ModNumber = {0, 1, 2, 3, 0, 1, 0, 0}, }; static const VK_TO_WCHARS2 vk_to_wchars2[] = From 92b3c7f3530f4bc9afb1d0a216419b37650370cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 10:26:37 +0100 Subject: [PATCH 068/758] win32u: Return the current display mode depth with nulldrv. (cherry picked from commit 8531f23a41956dcc3c68d6b02401f5c78db8ec60) --- dlls/win32u/driver.c | 2 +- dlls/win32u/sysparams.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 1c0708461a1..3f676dc5796 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -761,7 +761,7 @@ static BOOL nulldrv_GetCurrentDisplaySettings( LPCWSTR name, BOOL is_primary, LP static INT nulldrv_GetDisplayDepth( LPCWSTR name, BOOL is_primary ) { - return 32; + return -1; /* use default implementation */ } static BOOL nulldrv_UpdateDisplayDevices( const struct gdi_device_manager *manager, BOOL force, void *param ) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 826a9927e03..aeae43921e2 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2823,6 +2823,7 @@ static unsigned int active_monitor_count(void) INT get_display_depth( UNICODE_STRING *name ) { struct display_device *device; + BOOL is_primary; INT depth; if (!lock_display_devices()) @@ -2839,8 +2840,16 @@ INT get_display_depth( UNICODE_STRING *name ) return 32; } - depth = user_driver->pGetDisplayDepth( device->device_name, - !!(device->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE) ); + is_primary = !!(device->state_flags & DISPLAY_DEVICE_PRIMARY_DEVICE); + if ((depth = user_driver->pGetDisplayDepth( device->device_name, is_primary )) < 0) + { + struct adapter *adapter = CONTAINING_RECORD( device, struct adapter, dev ); + DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}; + + if (!adapter_get_current_settings( adapter, ¤t_mode )) depth = 32; + else depth = current_mode.dmBitsPerPel; + } + unlock_display_devices(); return depth; } From 416d1b384337c4f937ff243947b1f87246cf5242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 09:30:37 +0100 Subject: [PATCH 069/758] win32u: Check GUID_NULL display device if desktop atom is missing. When using nulldrv, there's no module to call __wine_set_user_driver and the user driver is still lazy_load_driver when get_display_driver gets called during desktop windows creation. Then, load_desktop_driver fails as it cannot find the not-yet-created desktop window atom, and fails later explorer.exe window creations such as the systray window. Other processes don't suffer from this as they wait for the desktop window to be fully created, and its atom will be eventually set. This change makes sure that we succeed in the case nulldrv was selected by explorer.exe, while still failing in case of error with another user driver as it would fail to open the right display device GUID. (cherry picked from commit 9c22a5ea63c6be01e257f05bbed23e03d5a7f407) --- dlls/win32u/driver.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 3f676dc5796..eb736de272d 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -923,6 +923,8 @@ static const WCHAR guid_key_suffixW[] = {'}','\\','0','0','0','0'}; static BOOL load_desktop_driver( HWND hwnd ) { + static const WCHAR guid_nullW[] = {'0','0','0','0','0','0','0','0','-','0','0','0','0','-','0','0','0','0','-', + '0','0','0','0','-','0','0','0','0','0','0','0','0','0','0','0','0',0}; WCHAR key[ARRAYSIZE(guid_key_prefixW) + 40 + ARRAYSIZE(guid_key_suffixW)], *ptr; char buf[4096]; KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf; @@ -946,9 +948,15 @@ static BOOL load_desktop_driver( HWND hwnd ) memcpy( key, guid_key_prefixW, sizeof(guid_key_prefixW) ); ptr = key + ARRAYSIZE(guid_key_prefixW); if (NtQueryInformationAtom( guid_atom, AtomBasicInformation, buf, sizeof(buf), NULL )) - return FALSE; - memcpy( ptr, abi->Name, abi->NameLength ); - ptr += abi->NameLength / sizeof(WCHAR); + { + wcscpy( ptr, guid_nullW ); + ptr += ARRAY_SIZE(guid_nullW) - 1; + } + else + { + memcpy( ptr, abi->Name, abi->NameLength ); + ptr += abi->NameLength / sizeof(WCHAR); + } memcpy( ptr, guid_key_suffixW, sizeof(guid_key_suffixW) ); ptr += ARRAY_SIZE(guid_key_suffixW); From fd7adbafa4f2a4b2a207aea203e97ffff40292f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 13:11:04 +0100 Subject: [PATCH 070/758] win32u: Initialize IO_STATUS_BLOCK in load_directory_fonts. To avoid invalid writes on WOW64 Nt calls. (cherry picked from commit ec5d9f6413e6a1945c87651863d366e077cbe27e) --- dlls/win32u/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index da288010c0b..d079c41f35a 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -6548,9 +6548,9 @@ static void load_system_bitmap_fonts(void) static void load_directory_fonts( WCHAR *path, UINT flags ) { + IO_STATUS_BLOCK io = {{0}}; OBJECT_ATTRIBUTES attr; UNICODE_STRING nt_name; - IO_STATUS_BLOCK io; HANDLE handle; char buf[8192]; size_t len; From 5d1062ede3bc39fd39458c66d26cae58a5a1edcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 13:11:59 +0100 Subject: [PATCH 071/758] win32u: Initialize IO_STATUS_BLOCK in rawinput add_device. To avoid invalid writes on WOW64 Nt calls. (cherry picked from commit 647e20a9ff24216e61dcb9e9ee8c9ff73da812dd) --- dlls/win32u/rawinput.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index f27bc092102..e5eab049fa3 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -220,11 +220,11 @@ static struct device *add_device( HKEY key, DWORD type ) static const RID_DEVICE_INFO_MOUSE mouse_info = {1, 5, 0, FALSE}; struct hid_preparsed_data *preparsed = NULL; HID_COLLECTION_INFORMATION hid_info; + IO_STATUS_BLOCK io = {{0}}; OBJECT_ATTRIBUTES attr; UNICODE_STRING string; struct device *device; RID_DEVICE_INFO info; - IO_STATUS_BLOCK io; unsigned int status; UINT32 handle; void *buffer; From 6a0eeea068109349d0a6c6364d34a02a9593e2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 13:12:19 +0100 Subject: [PATCH 072/758] winex11: Initialize IO_STATUS_BLOCK in X11DRV_GetICMProfile. To avoid invalid writes on WOW64 Nt calls. (cherry picked from commit eca1e4ea3666d0f9290f3bc2fc75f3e44e42656c) --- dlls/winex11.drv/graphics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/graphics.c b/dlls/winex11.drv/graphics.c index fbc1c9cde1b..ab861dc6bc8 100644 --- a/dlls/winex11.drv/graphics.c +++ b/dlls/winex11.drv/graphics.c @@ -1690,7 +1690,7 @@ BOOL CDECL X11DRV_GetICMProfile( PHYSDEV dev, BOOL allow_default, LPDWORD size, else if ((buffer = get_icm_profile( &buflen ))) { static const WCHAR icm[] = {'.','i','c','m',0}; - IO_STATUS_BLOCK io; + IO_STATUS_BLOCK io = {{0}}; UINT64 hash = 0; HANDLE file; int status; From 92fdf7afa08999b3a217d09b3fca0ea95667025a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 11:46:22 +0100 Subject: [PATCH 073/758] maintainers: Assume maintainership of IME support. (cherry picked from commit bd7a1a4d6670a58bfcc18bb077b066b308900ac0) --- MAINTAINERS | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8c30f8c6d1d..3c4e7a308ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -181,8 +181,12 @@ F: dlls/win32u/rawinput.c F: server/queue.c Input methods -M: Aric Stewart +M: Rémi Bernon +P: Aric Stewart F: dlls/imm32/ +F: dlls/win32u/imm.c +F: dlls/winemac.drv/ime.c +F: dlls/winex11.drv/ime.c JavaScript M: Jacek Caban From c72241c53f72d4457db10e18356935de74f398b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Feb 2023 17:22:25 +0100 Subject: [PATCH 074/758] imm32/tests: Add broken test results for w10v22H2. (cherry picked from commit b46ad448ab8ac3bdd2291f0336ec53886e5e7a20) --- dlls/imm32/tests/imm32.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 98af84383ba..ff9b9b038fb 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2277,7 +2277,9 @@ static DWORD WINAPI com_initialization_thread(void *arg) static void test_com_initialization(void) { + APTTYPEQUALIFIER qualifier; HANDLE thread; + APTTYPE type; HRESULT hr; HWND wnd; BOOL r; @@ -2332,13 +2334,17 @@ static void test_com_initialization(void) ok(hr == S_OK, "CoInitialize returned %lx\n", hr); test_apttype(APTTYPE_MTA); DestroyWindow(wnd); - test_apttype(-1); + + hr = CoGetApartmentType(&type, &qualifier); + ok(hr == CO_E_NOTINITIALIZED || broken(hr == S_OK) /* w10v22H2 */, + "CoGetApartmentType returned %#lx\n", hr); + test_apttype(hr == S_OK ? APTTYPE_MTA : -1); wnd = CreateWindowA("static", "static", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, 0); ok(wnd != NULL, "CreateWindow failed\n"); - test_apttype(-1); + test_apttype(hr == S_OK ? APTTYPE_MTA : -1); ShowWindow(wnd, SW_SHOW); - test_apttype(APTTYPE_MAINSTA); + test_apttype(hr == S_OK ? APTTYPE_MTA : APTTYPE_MAINSTA); DestroyWindow(wnd); test_apttype(-1); } From edc9284d002d6c051bab97119ef7c1670fe76778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 4 Dec 2022 12:55:42 +0100 Subject: [PATCH 075/758] imm32/tests: Test ImmInstallIMEW with an actual IME. (cherry picked from commit d07a5e6b91352d9a44c8d23491432a6f446ada4b) --- dlls/imm32/tests/Makefile.in | 7 +- dlls/imm32/tests/ime_test.h | 35 ++++++++ dlls/imm32/tests/ime_wrapper.c | 107 +++++++++++++++++++++++ dlls/imm32/tests/ime_wrapper.rc | 28 ++++++ dlls/imm32/tests/ime_wrapper.spec | 16 ++++ dlls/imm32/tests/imm32.c | 138 +++++++++++++++++++++++++++++- 6 files changed, 327 insertions(+), 4 deletions(-) create mode 100644 dlls/imm32/tests/ime_test.h create mode 100644 dlls/imm32/tests/ime_wrapper.c create mode 100644 dlls/imm32/tests/ime_wrapper.rc create mode 100644 dlls/imm32/tests/ime_wrapper.spec diff --git a/dlls/imm32/tests/Makefile.in b/dlls/imm32/tests/Makefile.in index d0881429e34..ee4999f2855 100644 --- a/dlls/imm32/tests/Makefile.in +++ b/dlls/imm32/tests/Makefile.in @@ -1,5 +1,8 @@ TESTDLL = imm32.dll -IMPORTS = imm32 ole32 user32 +IMPORTS = imm32 ole32 user32 advapi32 -C_SRCS = \ +SOURCES = \ + ime_wrapper.c \ + ime_wrapper.rc \ + ime_wrapper.spec \ imm32.c diff --git a/dlls/imm32/tests/ime_test.h b/dlls/imm32/tests/ime_test.h new file mode 100644 index 00000000000..bdfb899b504 --- /dev/null +++ b/dlls/imm32/tests/ime_test.h @@ -0,0 +1,35 @@ +/* + * Copyright 2023 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 + */ + +#ifndef __WINE_IME_TEST_H +#define __WINE_IME_TEST_H + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wingdi.h" + +#include "imm.h" +#include "immdev.h" + +#endif /* __WINE_IME_TEST_H */ diff --git a/dlls/imm32/tests/ime_wrapper.c b/dlls/imm32/tests/ime_wrapper.c new file mode 100644 index 00000000000..5bdd9afa336 --- /dev/null +++ b/dlls/imm32/tests/ime_wrapper.c @@ -0,0 +1,107 @@ +/* + * Copyright 2023 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 "ime_test.h" + +BOOL WINAPI ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) +{ + return FALSE; +} + +DWORD WINAPI ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest, DWORD dest_len, UINT flag ) +{ + return 0; +} + +BOOL WINAPI ImeDestroy( UINT force ) +{ + return FALSE; +} + +UINT WINAPI ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style, + const WCHAR *string, void *data ) +{ + return 0; +} + +LRESULT WINAPI ImeEscape( HIMC himc, UINT escape, void *data ) +{ + return 0; +} + +DWORD WINAPI ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent, + IMEMENUITEMINFOW *menu, DWORD size ) +{ + return 0; +} + +UINT WINAPI ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style_buf ) +{ + return 0; +} + +BOOL WINAPI ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) +{ + return FALSE; +} + +BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) +{ + return FALSE; +} + +BOOL WINAPI ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) +{ + return FALSE; +} + +BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) +{ + return FALSE; +} + +BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag ) +{ + return FALSE; +} + +BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len, + const void *read, DWORD read_len ) +{ + return FALSE; +} + +UINT WINAPI ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) +{ + return 0; +} + +BOOL WINAPI ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) +{ + return FALSE; +} + +BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) +{ + return FALSE; +} + +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved ) +{ + return TRUE; +} diff --git a/dlls/imm32/tests/ime_wrapper.rc b/dlls/imm32/tests/ime_wrapper.rc new file mode 100644 index 00000000000..6844b62195b --- /dev/null +++ b/dlls/imm32/tests/ime_wrapper.rc @@ -0,0 +1,28 @@ +/* + * Copyright 2023 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 + */ + +#pragma makedep testdll + +#define WINE_LANGID 0400 +#define WINE_FILETYPE VFT_DRV +#define WINE_FILESUBTYPE VFT2_DRV_INPUTMETHOD +#define WINE_FILENAME "ime_wrapper" +#define WINE_FILENAME_STR "ime_wrapper.dll" +#define WINE_FILEDESCRIPTION_STR "WineTest IME" + +#include "wine/wine_common_ver.rc" diff --git a/dlls/imm32/tests/ime_wrapper.spec b/dlls/imm32/tests/ime_wrapper.spec new file mode 100644 index 00000000000..937fdba922a --- /dev/null +++ b/dlls/imm32/tests/ime_wrapper.spec @@ -0,0 +1,16 @@ +@ stdcall ImeConfigure(long long long ptr) +@ stdcall ImeConversionList(long wstr ptr long long) +@ stdcall ImeDestroy(long) +@ stdcall ImeEnumRegisterWord(ptr wstr long wstr ptr) +@ stdcall ImeEscape(long long ptr) +@ stdcall ImeGetImeMenuItems(long long long ptr ptr long) +@ stdcall ImeGetRegisterWordStyle(long ptr) +@ stdcall ImeInquire(ptr wstr wstr) +@ stdcall ImeProcessKey(long long long ptr) +@ stdcall ImeRegisterWord(wstr long wstr) +@ stdcall ImeSelect(long long) +@ stdcall ImeSetActiveContext(long long) +@ stdcall ImeSetCompositionString(long long ptr long ptr long) +@ stdcall ImeToAsciiEx(long long ptr ptr long long) +@ stdcall ImeUnregisterWord(wstr long wstr) +@ stdcall NotifyIME(long long long long) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ff9b9b038fb..127b0e90be8 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -18,7 +18,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" #include "wine/test.h" #include "objbase.h" @@ -27,6 +33,8 @@ #include "imm.h" #include "immdev.h" +#include "ime_test.h" + BOOL WINAPI ImmSetActiveContext(HWND, HIMC, BOOL); static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD); @@ -206,6 +214,28 @@ static HWND hwnd, child; static HWND get_ime_window(void); +static void load_resource( const WCHAR *name, WCHAR *filename ) +{ + static WCHAR path[MAX_PATH]; + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + GetTempPathW( ARRAY_SIZE(path), path ); + GetTempFileNameW( path, name, 0, filename ); + + file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "failed to create %s, error %lu\n", debugstr_w(filename), GetLastError() ); + + res = FindResourceW( NULL, name, L"TESTDLL" ); + ok( res != 0, "couldn't find resource\n" ); + ptr = LockResource( LoadResource( GetModuleHandleW( NULL ), res ) ); + WriteFile( file, ptr, SizeofResource( GetModuleHandleW( NULL ), res ), &written, NULL ); + ok( written == SizeofResource( GetModuleHandleW( NULL ), res ), "couldn't write resource\n" ); + CloseHandle( file ); +} + static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { HWND default_ime_wnd; @@ -2445,7 +2475,109 @@ static void test_ImmDisableIME(void) ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def); } -START_TEST(imm32) { +static UINT ime_count; +static WCHAR ime_path[MAX_PATH]; + +static HKL ime_install(void) +{ + WCHAR buffer[MAX_PATH]; + DWORD len, ret; + HKEY hkey; + HKL hkl; + + /* install the actual IME module, it will lookup the functions from the DLL */ + load_resource( L"ime_wrapper.dll", buffer ); + + SetLastError( 0xdeadbeef ); + swprintf( ime_path, ARRAY_SIZE(ime_path), L"c:\\windows\\system32\\wine%04x.ime", ime_count++ ); + ret = MoveFileW( buffer, ime_path ); + todo_wine_if( GetLastError() == ERROR_ALREADY_EXISTS ) + ok( ret || broken( !ret ) /* sometimes still in use */, + "MoveFileW failed, error %lu\n", GetLastError() ); + + hkl = ImmInstallIMEW( ime_path, L"WineTest IME" ); + todo_wine + ok( hkl == (HKL)(int)0xe0200400, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() ); + + swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl ); + ret = RegOpenKeyW( HKEY_LOCAL_MACHINE, buffer, &hkey ); + ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() ); + + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = RegQueryValueExW( hkey, L"Ime File", NULL, NULL, (BYTE *)buffer, &len ); + ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() ); + todo_wine + ok( !wcsicmp( buffer, wcsrchr( ime_path, '\\' ) + 1 ), "got Ime File %s\n", debugstr_w(buffer) ); + + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = RegQueryValueExW( hkey, L"Layout Text", NULL, NULL, (BYTE *)buffer, &len ); + ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() ); + ok( !wcscmp( buffer, L"WineTest IME" ), "got Layout Text %s\n", debugstr_w(buffer) ); + + len = sizeof(buffer); + memset( buffer, 0xcd, sizeof(buffer) ); + ret = RegQueryValueExW( hkey, L"Layout File", NULL, NULL, (BYTE *)buffer, &len ); + todo_wine + ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() ); + todo_wine + ok( !wcscmp( buffer, L"kbdus.dll" ), "got Layout File %s\n", debugstr_w(buffer) ); + + ret = RegCloseKey( hkey ); + ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() ); + + return hkl; +} + +static void ime_cleanup( HKL hkl ) +{ + WCHAR buffer[MAX_PATH], value[MAX_PATH]; + DWORD i, buffer_len, value_len, ret; + HKEY hkey; + + ret = UnloadKeyboardLayout( hkl ); + todo_wine + ok( ret, "UnloadKeyboardLayout failed, error %lu\n", GetLastError() ); + + swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl ); + ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, buffer ); + ok( !ret, "RegDeleteKeyW returned %#lx, error %lu\n", ret, GetLastError() ); + + ret = RegOpenKeyW( HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkey ); + ok( !ret, "RegOpenKeyW returned %#lx, error %lu\n", ret, GetLastError() ); + + value_len = ARRAY_SIZE(value); + buffer_len = sizeof(buffer); + for (i = 0; !RegEnumValueW( hkey, i, value, &value_len, NULL, NULL, (void *)buffer, &buffer_len ); i++) + { + value_len = ARRAY_SIZE(value); + buffer_len = sizeof(buffer); + if (hkl != UlongToHandle( wcstoul( buffer, NULL, 16 ) )) continue; + ret = RegDeleteValueW( hkey, value ); + ok( !ret, "RegDeleteValueW returned %#lx, error %lu\n", ret, GetLastError() ); + } + + ret = RegCloseKey( hkey ); + ok( !ret, "RegCloseKey returned %#lx, error %lu\n", ret, GetLastError() ); + + ret = DeleteFileW( ime_path ); + todo_wine_if( GetLastError() == ERROR_ACCESS_DENIED ) + ok( ret || broken( !ret ) /* sometimes still in use */, + "DeleteFileW failed, error %lu\n", GetLastError() ); +} + +static void test_ImmInstallIME(void) +{ + HKL hkl; + + if (!(hkl = ime_install())) return; + + ime_cleanup( hkl ); +} + +START_TEST(imm32) +{ if (!is_ime_enabled()) { win_skip("IME support not implemented\n"); @@ -2454,6 +2586,8 @@ START_TEST(imm32) { test_com_initialization(); + test_ImmInstallIME(); + if (init()) { test_ImmNotifyIME(); From e2f85534d1d86e21e1e0f83063de7a7acc14afe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 19:53:40 +0100 Subject: [PATCH 076/758] imm32/tests: Redirect IME function to the main module. For easier testing and to wokaround some Windows IME caching mechanism that prevent the IME module from reloading. (cherry picked from commit 16222f2de6e0b4619730467a66b301b62050b90a) --- dlls/imm32/tests/ime_test.h | 21 +++ dlls/imm32/tests/ime_wrapper.c | 67 +++++++--- dlls/imm32/tests/ime_wrapper.spec | 1 + dlls/imm32/tests/imm32.c | 210 ++++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+), 16 deletions(-) diff --git a/dlls/imm32/tests/ime_test.h b/dlls/imm32/tests/ime_test.h index bdfb899b504..fda8065276d 100644 --- a/dlls/imm32/tests/ime_test.h +++ b/dlls/imm32/tests/ime_test.h @@ -32,4 +32,25 @@ #include "imm.h" #include "immdev.h" +struct ime_functions +{ + BOOL (*WINAPI pImeConfigure)(HKL,HWND,DWORD,void *); + DWORD (*WINAPI pImeConversionList)(HIMC,const WCHAR *,CANDIDATELIST *,DWORD,UINT); + BOOL (*WINAPI pImeDestroy)(UINT); + UINT (*WINAPI pImeEnumRegisterWord)(REGISTERWORDENUMPROCW,const WCHAR *,DWORD,const WCHAR *,void *); + LRESULT (*WINAPI pImeEscape)(HIMC,UINT,void *); + DWORD (*WINAPI pImeGetImeMenuItems)(HIMC,DWORD,DWORD,IMEMENUITEMINFOW *,IMEMENUITEMINFOW *,DWORD); + UINT (*WINAPI pImeGetRegisterWordStyle)(UINT,STYLEBUFW *); + BOOL (*WINAPI pImeInquire)(IMEINFO *,WCHAR *,DWORD); + BOOL (*WINAPI pImeProcessKey)(HIMC,UINT,LPARAM,BYTE *); + BOOL (*WINAPI pImeRegisterWord)(const WCHAR *,DWORD,const WCHAR *); + BOOL (*WINAPI pImeSelect)(HIMC,BOOL); + BOOL (*WINAPI pImeSetActiveContext)(HIMC,BOOL); + BOOL (*WINAPI pImeSetCompositionString)(HIMC,DWORD,const void *,DWORD,const void *,DWORD); + UINT (*WINAPI pImeToAsciiEx)(UINT,UINT,BYTE *,TRANSMSGLIST *,UINT,HIMC); + BOOL (*WINAPI pImeUnregisterWord)(const WCHAR *,DWORD,const WCHAR *); + BOOL (*WINAPI pNotifyIME)(HIMC,DWORD,DWORD,DWORD); + BOOL (*WINAPI pDllMain)(HINSTANCE,DWORD,void *); +}; + #endif /* __WINE_IME_TEST_H */ diff --git a/dlls/imm32/tests/ime_wrapper.c b/dlls/imm32/tests/ime_wrapper.c index 5bdd9afa336..d8a03499549 100644 --- a/dlls/imm32/tests/ime_wrapper.c +++ b/dlls/imm32/tests/ime_wrapper.c @@ -18,90 +18,125 @@ #include "ime_test.h" +struct ime_functions ime_functions = {0}; + BOOL WINAPI ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) { - return FALSE; + if (!ime_functions.pImeConfigure) return FALSE; + return ime_functions.pImeConfigure( hkl, hwnd, mode, data ); } DWORD WINAPI ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest, DWORD dest_len, UINT flag ) { - return 0; + if (!ime_functions.pImeConversionList) return 0; + return ime_functions.pImeConversionList( himc, source, dest, dest_len, flag ); } BOOL WINAPI ImeDestroy( UINT force ) { - return FALSE; + if (!ime_functions.pImeDestroy) return FALSE; + return ime_functions.pImeDestroy( force ); } UINT WINAPI ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style, const WCHAR *string, void *data ) { - return 0; + if (!ime_functions.pImeEnumRegisterWord) return 0; + return ime_functions.pImeEnumRegisterWord( proc, reading, style, string, data ); } LRESULT WINAPI ImeEscape( HIMC himc, UINT escape, void *data ) { - return 0; + if (!ime_functions.pImeEscape) return 0; + return ime_functions.pImeEscape( himc, escape, data ); } DWORD WINAPI ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent, IMEMENUITEMINFOW *menu, DWORD size ) { - return 0; + if (!ime_functions.pImeGetImeMenuItems) return 0; + return ime_functions.pImeGetImeMenuItems( himc, flags, type, parent, menu, size ); } UINT WINAPI ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style_buf ) { - return 0; + if (!ime_functions.pImeGetRegisterWordStyle) return 0; + return ime_functions.pImeGetRegisterWordStyle( item, style_buf ); } BOOL WINAPI ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) { - return FALSE; + if (!ime_functions.pImeInquire) return FALSE; + return ime_functions.pImeInquire( info, ui_class, flags ); } BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) { - return FALSE; + if (!ime_functions.pImeProcessKey) return FALSE; + return ime_functions.pImeProcessKey( himc, vkey, key_data, key_state ); } BOOL WINAPI ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) { - return FALSE; + if (!ime_functions.pImeRegisterWord) return FALSE; + return ime_functions.pImeRegisterWord( reading, style, string ); } BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) { - return FALSE; + if (!ime_functions.pImeSelect) return FALSE; + return ime_functions.pImeSelect( himc, select ); } BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag ) { - return FALSE; + if (!ime_functions.pImeSetActiveContext) return FALSE; + return ime_functions.pImeSetActiveContext( himc, flag ); } BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len, const void *read, DWORD read_len ) { - return FALSE; + if (!ime_functions.pImeSetCompositionString) return FALSE; + return ime_functions.pImeSetCompositionString( himc, index, comp, comp_len, read, read_len ); } UINT WINAPI ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) { - return 0; + if (!ime_functions.pImeToAsciiEx) return 0; + return ime_functions.pImeToAsciiEx( vkey, scan_code, key_state, msgs, state, himc ); } BOOL WINAPI ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) { - return FALSE; + if (!ime_functions.pImeUnregisterWord) return FALSE; + return ime_functions.pImeUnregisterWord( reading, style, string ); } BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) { - return FALSE; + if (!ime_functions.pNotifyIME) return FALSE; + return ime_functions.pNotifyIME( himc, action, index, value ); } BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved ) { + static HMODULE module; + + switch (reason) + { + case DLL_PROCESS_ATTACH: + if (!(module = GetModuleHandleW( L"winetest_ime.dll" ))) return TRUE; + ime_functions = *(struct ime_functions *)GetProcAddress( module, "ime_functions" ); + if (!ime_functions.pDllMain) return TRUE; + return ime_functions.pDllMain( instance, reason, reserved ); + + case DLL_PROCESS_DETACH: + if (module == instance) return TRUE; + if (!ime_functions.pDllMain) return TRUE; + ime_functions.pDllMain( instance, reason, reserved ); + memset( &ime_functions, 0, sizeof(ime_functions) ); + } + return TRUE; } diff --git a/dlls/imm32/tests/ime_wrapper.spec b/dlls/imm32/tests/ime_wrapper.spec index 937fdba922a..05a60e84a5d 100644 --- a/dlls/imm32/tests/ime_wrapper.spec +++ b/dlls/imm32/tests/ime_wrapper.spec @@ -14,3 +14,4 @@ @ stdcall ImeToAsciiEx(long long ptr ptr long long) @ stdcall ImeUnregisterWord(wstr long wstr) @ stdcall NotifyIME(long long long long) +@ extern -private ime_functions diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 127b0e90be8..0fd88d65aac 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2475,16 +2475,219 @@ static void test_ImmDisableIME(void) ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def); } +#define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) + +static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + ok( 0, "unexpected call\n" ); + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static WNDCLASSEXW ime_ui_class = +{ + .cbSize = sizeof(WNDCLASSEXW), + .style = CS_IME, + .lpfnWndProc = ime_ui_window_proc, + .cbWndExtra = 2 * sizeof(LONG_PTR), + .lpszClassName = L"WineTestIME", +}; + +static BOOL WINAPI ime_ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) +{ + ime_trace( "hkl %p, hwnd %p, mode %lu, data %p\n", hkl, hwnd, mode, data ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static DWORD WINAPI ime_ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest, + DWORD dest_len, UINT flag ) +{ + ime_trace( "himc %p, source %s, dest %p, dest_len %lu, flag %#x\n", + himc, debugstr_w(source), dest, dest_len, flag ); + ok( 0, "unexpected call\n" ); + return 0; +} + +static BOOL WINAPI ime_ImeDestroy( UINT force ) +{ + ime_trace( "force %u\n", force ); + ok( 0, "unexpected call\n" ); + return TRUE; +} + +static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style, + const WCHAR *string, void *data ) +{ + ime_trace( "proc %p, reading %s, style %lu, string %s, data %p\n", + proc, debugstr_w(reading), style, debugstr_w(string), data ); + ok( 0, "unexpected call\n" ); + return 0; +} + +static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data ) +{ + ime_trace( "himc %p, escape %#x, data %p\n", himc, escape, data ); + ok( 0, "unexpected call\n" ); + return TRUE; +} + +static DWORD WINAPI ime_ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent, + IMEMENUITEMINFOW *menu, DWORD size ) +{ + ime_trace( "himc %p, flags %#lx, type %lu, parent %p, menu %p, size %#lx\n", + himc, flags, type, parent, menu, size ); + ok( 0, "unexpected call\n" ); + return 0; +} + +static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style ) +{ + ime_trace( "item %u, style %p\n", item, style ); + ok( 0, "unexpected call\n" ); + return 0; +} + +static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) +{ + ime_trace( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags ); + ok( 0, "unexpected call\n" ); + return TRUE; +} + +static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) +{ + ime_trace( "himc %p, vkey %u, key_data %#Ix, key_state %p\n", + himc, vkey, key_data, key_state ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) +{ + ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select ) +{ + ime_trace( "himc %p, select %d\n", himc, select ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static BOOL WINAPI ime_ImeSetActiveContext( HIMC himc, BOOL flag ) +{ + ime_trace( "himc %p, flag %#x\n", himc, flag ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len, + const void *read, DWORD read_len ) +{ + ime_trace( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu\n", + himc, index, comp, comp_len, read, read_len ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) +{ + ime_trace( "vkey %u, scan_code %u, key_state %p, msgs %p, state %u, himc %p\n", + vkey, scan_code, key_state, msgs, state, himc ); + ok( 0, "unexpected call\n" ); + return 0; +} + +static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) +{ + ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static BOOL WINAPI ime_NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) +{ + ime_trace( "himc %p, action %lu, index %lu, value %lu\n", himc, action, index, value ); + ok( 0, "unexpected call\n" ); + return FALSE; +} + +static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved ) +{ + ime_trace( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved ); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( instance ); + ime_ui_class.hInstance = instance; + RegisterClassExW( &ime_ui_class ); + break; + + case DLL_PROCESS_DETACH: + UnregisterClassW( ime_ui_class.lpszClassName, instance ); + break; + } + + return TRUE; +} + +static struct ime_functions ime_functions = +{ + ime_ImeConfigure, + ime_ImeConversionList, + ime_ImeDestroy, + ime_ImeEnumRegisterWord, + ime_ImeEscape, + ime_ImeGetImeMenuItems, + ime_ImeGetRegisterWordStyle, + ime_ImeInquire, + ime_ImeProcessKey, + ime_ImeRegisterWord, + ime_ImeSelect, + ime_ImeSetActiveContext, + ime_ImeSetCompositionString, + ime_ImeToAsciiEx, + ime_ImeUnregisterWord, + ime_NotifyIME, + ime_DllMain, +}; + static UINT ime_count; static WCHAR ime_path[MAX_PATH]; static HKL ime_install(void) { WCHAR buffer[MAX_PATH]; + HMODULE module; DWORD len, ret; HKEY hkey; HKL hkl; + /* IME module gets cached and won't reload from disk as soon as a window has + * loaded it. To workaround the issue we load the module first as a DLL, + * set its function pointers from the test, and later when the cached IME + * gets loaded, read the function pointers from the separately loaded DLL. + */ + + load_resource( L"ime_wrapper.dll", buffer ); + + SetLastError( 0xdeadbeef ); + ret = MoveFileW( buffer, L"c:\\windows\\system32\\winetest_ime.dll" ); + if (!ret) + { + ok( GetLastError() == ERROR_ACCESS_DENIED, "got error %lu\n", GetLastError() ); + win_skip( "Failed to copy DLL to system directory\n" ); + return 0; + } + + module = LoadLibraryW( L"c:\\windows\\system32\\winetest_ime.dll" ); + ok( !!module, "LoadLibraryW failed, error %lu\n", GetLastError() ); + *(struct ime_functions *)GetProcAddress( module, "ime_functions" ) = ime_functions; + /* install the actual IME module, it will lookup the functions from the DLL */ load_resource( L"ime_wrapper.dll", buffer ); @@ -2532,6 +2735,7 @@ static HKL ime_install(void) static void ime_cleanup( HKL hkl ) { + HMODULE module = GetModuleHandleW( L"winetest_ime.dll" ); WCHAR buffer[MAX_PATH], value[MAX_PATH]; DWORD i, buffer_len, value_len, ret; HKEY hkey; @@ -2565,6 +2769,12 @@ static void ime_cleanup( HKL hkl ) todo_wine_if( GetLastError() == ERROR_ACCESS_DENIED ) ok( ret || broken( !ret ) /* sometimes still in use */, "DeleteFileW failed, error %lu\n", GetLastError() ); + + ret = FreeLibrary( module ); + ok( ret, "FreeLibrary failed, error %lu\n", GetLastError() ); + + ret = DeleteFileW( L"c:\\windows\\system32\\winetest_ime.dll" ); + ok( ret, "DeleteFileW failed, error %lu\n", GetLastError() ); } static void test_ImmInstallIME(void) From 9a7af057d8cf0f22dcf68abeed0df2a38a11adfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 19:19:06 +0100 Subject: [PATCH 077/758] imm32/tests: Test ImmGetDescription with the installed IME. (cherry picked from commit d0cc3ded0854c5140612d858b854404e70ef7470) --- dlls/imm32/tests/imm32.c | 136 +++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 56 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 0fd88d65aac..9e24f899f3f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1186,61 +1186,6 @@ static void test_ImmGetContext(void) ok(ImmReleaseContext(hwnd, himc), "ImmReleaseContext failed\n"); } -static void test_ImmGetDescription(void) -{ - HKL hkl; - WCHAR descW[100]; - CHAR descA[100]; - UINT ret, lret; - - /* FIXME: invalid keyboard layouts should not pass */ - ret = ImmGetDescriptionW(NULL, NULL, 0); - ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret); - ret = ImmGetDescriptionA(NULL, NULL, 0); - ok(!ret, "ImmGetDescriptionA failed, expected 0 received %d.\n", ret); - - /* load a language with valid IMM descriptions */ - hkl = GetKeyboardLayout(0); - ok(hkl != 0, "GetKeyboardLayout failed, expected != 0.\n"); - - ret = ImmGetDescriptionW(hkl, NULL, 0); - if(!ret) - { - win_skip("ImmGetDescriptionW is not working for current loaded keyboard.\n"); - return; - } - - SetLastError(0xdeadcafe); - ret = ImmGetDescriptionW(0, NULL, 100); - ok (ret == 0, "ImmGetDescriptionW with 0 hkl should return 0\n"); - ret = GetLastError(); - ok (ret == 0xdeadcafe, "Last Error should remain unchanged\n"); - - ret = ImmGetDescriptionW(hkl, descW, 0); - ok(ret, "ImmGetDescriptionW failed, expected != 0 received 0.\n"); - - lret = ImmGetDescriptionW(hkl, descW, ret + 1); - ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n"); - ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret); - - lret = ImmGetDescriptionA(hkl, descA, ret + 1); - ok(lret, "ImmGetDescriptionA failed, expected != 0 received 0.\n"); - ok(lret == ret, "ImmGetDescriptionA failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret); - - ret /= 2; /* try to copy partially */ - lret = ImmGetDescriptionW(hkl, descW, ret + 1); - ok(lret, "ImmGetDescriptionW failed, expected != 0 received 0.\n"); - ok(lret == ret, "ImmGetDescriptionW failed to return the correct amount of data. Expected %d, got %d.\n", ret, lret); - - lret = ImmGetDescriptionA(hkl, descA, ret + 1); - ok(!lret, "ImmGetDescriptionA should fail\n"); - - ret = ImmGetDescriptionW(hkl, descW, 1); - ok(!ret, "ImmGetDescriptionW failed, expected 0 received %d.\n", ret); - - UnloadKeyboardLayout(hkl); -} - static LRESULT (WINAPI *old_imm_wnd_proc)(HWND, UINT, WPARAM, LPARAM); static LRESULT WINAPI imm_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { @@ -2786,6 +2731,85 @@ static void test_ImmInstallIME(void) ime_cleanup( hkl ); } +static void test_ImmGetDescription(void) +{ + HKL hkl = GetKeyboardLayout( 0 ); + WCHAR bufferW[MAX_PATH]; + char bufferA[MAX_PATH]; + DWORD ret; + + SetLastError( 0xdeadbeef ); + ret = ImmGetDescriptionW( NULL, NULL, 0 ); + ok( !ret, "ImmGetDescriptionW returned %lu\n", ret ); + ret = ImmGetDescriptionA( NULL, NULL, 0 ); + ok( !ret, "ImmGetDescriptionA returned %lu\n", ret ); + ret = ImmGetDescriptionW( NULL, NULL, 100 ); + ok( !ret, "ImmGetDescriptionW returned %lu\n", ret ); + ret = ImmGetDescriptionA( NULL, NULL, 100 ); + ok( !ret, "ImmGetDescriptionA returned %lu\n", ret ); + ret = ImmGetDescriptionW( hkl, bufferW, 100 ); + todo_wine + ok( !ret, "ImmGetDescriptionW returned %lu\n", ret ); + ret = ImmGetDescriptionA( hkl, bufferA, 100 ); + todo_wine + ok( !ret, "ImmGetDescriptionA returned %lu\n", ret ); + ret = GetLastError(); + ok( ret == 0xdeadbeef, "got error %lu\n", ret ); + + if (!(hkl = ime_install())) return; + + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetDescriptionW( hkl, bufferW, 2 ); + ok( ret == 1, "ImmGetDescriptionW returned %lu\n", ret ); + ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetDescriptionA( hkl, bufferA, 2 ); + ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); + + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetDescriptionW( hkl, bufferW, 11 ); + todo_wine + ok( ret == 10, "ImmGetDescriptionW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, L"WineTest I" ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetDescriptionA( hkl, bufferA, 11 ); + todo_wine + ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); + + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetDescriptionW( hkl, bufferW, 12 ); + todo_wine + ok( ret == 11, "ImmGetDescriptionW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, L"WineTest IM" ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetDescriptionA( hkl, bufferA, 12 ); + todo_wine + ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); + + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetDescriptionW( hkl, bufferW, 13 ); + todo_wine + ok( ret == 12, "ImmGetDescriptionW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, L"WineTest IME" ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetDescriptionA( hkl, bufferA, 13 ); + todo_wine + ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); + + ime_cleanup( hkl ); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -2797,6 +2821,7 @@ START_TEST(imm32) test_com_initialization(); test_ImmInstallIME(); + test_ImmGetDescription(); if (init()) { @@ -2809,7 +2834,6 @@ START_TEST(imm32) test_ImmThreads(); test_ImmIsUIMessage(); test_ImmGetContext(); - test_ImmGetDescription(); test_ImmDefaultHwnd(); test_default_ime_window_creation(); test_ImmGetIMCLockCount(); From ab150a8a9cbbdcface86eb4f72498cfab5cc979d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 18 Feb 2023 10:33:33 +0100 Subject: [PATCH 078/758] imm32/tests: Test ImmGetIMEFileName with the installed IME. (cherry picked from commit 313611532405f9b0ac894d42f09832711105f678) --- dlls/imm32/tests/imm32.c | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 9e24f899f3f..97f3a5394ea 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2810,6 +2810,89 @@ static void test_ImmGetDescription(void) ime_cleanup( hkl ); } +static void test_ImmGetIMEFileName(void) +{ + HKL hkl = GetKeyboardLayout( 0 ); + WCHAR bufferW[MAX_PATH], expectW[16]; + char bufferA[MAX_PATH], expectA[16]; + DWORD ret; + + SetLastError( 0xdeadbeef ); + ret = ImmGetIMEFileNameW( NULL, NULL, 0 ); + ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret ); + ret = ImmGetIMEFileNameA( NULL, NULL, 0 ); + ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret ); + ret = ImmGetIMEFileNameW( NULL, NULL, 100 ); + ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret ); + ret = ImmGetIMEFileNameA( NULL, NULL, 100 ); + ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret ); + ret = ImmGetIMEFileNameW( hkl, bufferW, 100 ); + ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret ); + ret = ImmGetIMEFileNameA( hkl, bufferA, 100 ); + ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret ); + ret = GetLastError(); + todo_wine + ok( ret == 0xdeadbeef, "got error %lu\n", ret ); + + if (!(hkl = ime_install())) return; + + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetIMEFileNameW( hkl, bufferW, 2 ); + todo_wine + ok( ret == 1, "ImmGetIMEFileNameW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetIMEFileNameA( hkl, bufferA, 2 ); + ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); + + swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.I", ime_count - 1 ); + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetIMEFileNameW( hkl, bufferW, 11 ); + todo_wine + ok( ret == 10, "ImmGetIMEFileNameW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetIMEFileNameA( hkl, bufferA, 11 ); + ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); + + swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IM", ime_count - 1 ); + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetIMEFileNameW( hkl, bufferW, 12 ); + todo_wine + ok( ret == 11, "ImmGetIMEFileNameW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); + snprintf( expectA, ARRAY_SIZE(expectA), "WINE%04X.IME", ime_count - 1 ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetIMEFileNameA( hkl, bufferA, 12 ); + todo_wine + ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); + + swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IME", ime_count - 1 ); + memset( bufferW, 0xcd, sizeof(bufferW) ); + ret = ImmGetIMEFileNameW( hkl, bufferW, 13 ); + todo_wine + ok( ret == 12, "ImmGetIMEFileNameW returned %lu\n", ret ); + todo_wine + ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + ret = ImmGetIMEFileNameA( hkl, bufferA, 13 ); + todo_wine + ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); + todo_wine + ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); + + ime_cleanup( hkl ); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -2822,6 +2905,7 @@ START_TEST(imm32) test_ImmInstallIME(); test_ImmGetDescription(); + test_ImmGetIMEFileName(); if (init()) { From 7a15d72a0f62ddd7cde8de41e3db9d0a037f1d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 1 Mar 2023 22:57:29 +0100 Subject: [PATCH 079/758] user32/tests: Skip tests if layout failed to activate. (cherry picked from commit 9f072a273d35d29bd5ae09b9c6ba00ffd63236a6) --- dlls/user32/tests/input.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 1a9757bd1fb..fa659a3834a 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3307,7 +3307,20 @@ static void test_keyboard_layout_name(void) for (i = len - 1; i >= 0; --i) { id = (DWORD_PTR)layouts[i]; + + winetest_push_context( "%08lx", id ); + ActivateKeyboardLayout(layouts[i], 0); + + tmplayout = GetKeyboardLayout(0); + todo_wine_if(tmplayout != layouts[i]) + ok( tmplayout == layouts[i], "Failed to activate keyboard layout\n"); + if (tmplayout != layouts[i]) + { + winetest_pop_context(); + continue; + } + GetKeyboardLayoutNameW(klid); for (j = 0; j < len; ++j) @@ -3346,6 +3359,8 @@ static void test_keyboard_layout_name(void) /* The layout name only depends on the keyboard layout: the high word of HKL. */ GetKeyboardLayoutNameW(tmpklid); ok(!wcsicmp(klid, tmpklid), "GetKeyboardLayoutNameW returned %s, expected %s\n", debugstr_w(tmpklid), debugstr_w(klid)); + + winetest_pop_context(); } ActivateKeyboardLayout(layout, 0); From 19143f4c173221b3fda8561ffa0adbbb9a59e106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 1 Mar 2023 22:57:29 +0100 Subject: [PATCH 080/758] user32/tests: Add a WM_INPUTLANGCHANGE message test. (cherry picked from commit 3a4859e22db45289f9b02ea9ab1ccc150b638d19) --- dlls/user32/tests/input.c | 173 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index fa659a3834a..5cb02443e44 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3369,6 +3369,178 @@ static void test_keyboard_layout_name(void) free(layouts_preload); } +static HKL expect_hkl; +static HKL change_hkl; +static int got_setfocus; + +static LRESULT CALLBACK test_ActivateKeyboardLayout_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + ok( msg != WM_INPUTLANGCHANGEREQUEST, "got WM_INPUTLANGCHANGEREQUEST\n" ); + + if (msg == WM_SETFOCUS) got_setfocus = 1; + if (msg == WM_INPUTLANGCHANGE) + { + HKL layout = GetKeyboardLayout( 0 ); + CHARSETINFO info; + WCHAR klidW[64]; + UINT codepage; + LCID lcid; + + /* get keyboard layout lcid from its name, as the HKL might be aliased */ + GetKeyboardLayoutNameW( klidW ); + swscanf( klidW, L"%x", &lcid ); + lcid = LOWORD(lcid); + + if (!(HIWORD(layout) & 0x8000)) ok( lcid == HIWORD(layout), "got lcid %#lx\n", lcid ); + + GetLocaleInfoA( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (char *)&codepage, sizeof(codepage) ); + TranslateCharsetInfo( UlongToPtr( codepage ), &info, TCI_SRCCODEPAGE ); + + ok( !got_setfocus, "got WM_SETFOCUS before WM_INPUTLANGCHANGE\n" ); + ok( layout == expect_hkl, "got layout %p\n", layout ); + ok( wparam == info.ciCharset || broken(wparam == 0 && (HIWORD(layout) & 0x8000)), + "got wparam %#Ix\n", wparam ); + ok( lparam == (LPARAM)expect_hkl, "got lparam %#Ix\n", lparam ); + change_hkl = (HKL)lparam; + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static DWORD CALLBACK test_ActivateKeyboardLayout_thread_proc( void *arg ) +{ + ActivateKeyboardLayout( arg, 0 ); + return 0; +} + +static void test_ActivateKeyboardLayout( char **argv ) +{ + HKL layout, tmp_layout, *layouts; + HWND hwnd1, hwnd2; + HANDLE thread; + UINT i, count; + DWORD ret; + + layout = GetKeyboardLayout( 0 ); + count = GetKeyboardLayoutList( 0, NULL ); + ok( count > 0, "GetKeyboardLayoutList returned %d\n", count ); + layouts = malloc( count * sizeof(HKL) ); + ok( layouts != NULL, "Could not allocate memory\n" ); + count = GetKeyboardLayoutList( count, layouts ); + ok( count > 0, "GetKeyboardLayoutList returned %d\n", count ); + + hwnd1 = CreateWindowA( "static", "static", WS_VISIBLE | WS_POPUP, + 100, 100, 100, 100, 0, NULL, NULL, NULL ); + ok( !!hwnd1, "CreateWindow failed, error %lu\n", GetLastError() ); + empty_message_queue(); + + SetWindowLongPtrA( hwnd1, GWLP_WNDPROC, (LONG_PTR)test_ActivateKeyboardLayout_window_proc ); + + for (i = 0; i < count; ++i) + { + BOOL broken_focus_activate = FALSE; + HKL other_layout = layouts[i]; + + winetest_push_context( "%08x / %08x", (UINT)(UINT_PTR)layout, (UINT)(UINT_PTR)other_layout ); + + /* test WM_INPUTLANGCHANGE message */ + + change_hkl = 0; + expect_hkl = other_layout; + got_setfocus = 0; + ActivateKeyboardLayout( other_layout, 0 ); + if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + else todo_wine ok( change_hkl == other_layout, "got change_hkl %p\n", change_hkl ); + change_hkl = expect_hkl = 0; + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + /* changing the layout from another thread doesn't send the message */ + + thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); + ret = WaitForSingleObject( thread, 1000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + CloseHandle( thread ); + + /* and has no immediate effect */ + + empty_message_queue(); + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + /* but the change only takes effect after focus changes */ + + hwnd2 = CreateWindowA( "static", "static", WS_VISIBLE | WS_POPUP, + 100, 100, 100, 100, 0, NULL, NULL, NULL ); + ok( !!hwnd2, "CreateWindow failed, error %lu\n", GetLastError() ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == layout || broken(layout != other_layout && tmp_layout == other_layout) /* w7u */, + "got tmp_layout %p\n", tmp_layout ); + if (broken(layout != other_layout && tmp_layout == other_layout)) + { + win_skip( "Broken layout activation on focus change, skipping some tests\n" ); + broken_focus_activate = TRUE; + } + empty_message_queue(); + + /* only the focused window receives the WM_INPUTLANGCHANGE message */ + + ActivateKeyboardLayout( other_layout, 0 ); + ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); + ret = WaitForSingleObject( thread, 1000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + CloseHandle( thread ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + /* changing focus is enough for the layout change to take effect */ + + change_hkl = 0; + expect_hkl = layout; + got_setfocus = 0; + SetFocus( hwnd1 ); + + if (broken_focus_activate) + { + ok( got_setfocus == 1, "got got_setfocus %d\n", got_setfocus ); + ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + got_setfocus = 0; + ActivateKeyboardLayout( layout, 0 ); + } + + if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + else todo_wine ok( change_hkl == layout, "got change_hkl %p\n", change_hkl ); + change_hkl = expect_hkl = 0; + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == layout, "got tmp_layout %p\n", tmp_layout ); + + DestroyWindow( hwnd2 ); + empty_message_queue(); + + winetest_pop_context(); + } + + DestroyWindow( hwnd1 ); + + free( layouts ); +} + static void test_key_names(void) { char buffer[40]; @@ -4937,6 +5109,7 @@ START_TEST(input) test_ToAscii(); test_get_async_key_state(); test_keyboard_layout_name(); + test_ActivateKeyboardLayout( argv ); test_key_names(); test_attach_input(); test_GetKeyState(); From 0b9bf1dc142b418364209bd3359f0456c5505305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:39:07 +0200 Subject: [PATCH 081/758] win32u: Move window query functions around. (cherry picked from commit 30aa9055a0b59887cd2ef35e180ed07a58f98790) --- dlls/win32u/input.c | 86 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 9f17b71e0fe..9575a0a1172 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -534,6 +534,49 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const BOOL enable_mouse_in_pointer = FALSE; +/******************************************************************* + * NtUserGetForegroundWindow (win32u.@) + */ +HWND WINAPI NtUserGetForegroundWindow(void) +{ + volatile struct input_shared_memory *shared = get_foreground_shared_memory(); + HWND ret = 0; + + if (!shared) return 0; + + SHARED_READ_BEGIN( &shared->seq ) + { + ret = wine_server_ptr_handle( shared->active ); + } + SHARED_READ_END( &shared->seq ); + + return ret; +} + +/* see GetActiveWindow */ +HWND get_active_window(void) +{ + GUITHREADINFO info; + info.cbSize = sizeof(info); + return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0; +} + +/* see GetCapture */ +HWND get_capture(void) +{ + GUITHREADINFO info; + info.cbSize = sizeof(info); + return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0; +} + +/* see GetFocus */ +HWND get_focus(void) +{ + GUITHREADINFO info; + info.cbSize = sizeof(info); + return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0; +} + /********************************************************************** * NtUserAttachThreadInput (win32u.@) */ @@ -1711,49 +1754,6 @@ BOOL WINAPI release_capture(void) return ret; } -/******************************************************************* - * NtUserGetForegroundWindow (win32u.@) - */ -HWND WINAPI NtUserGetForegroundWindow(void) -{ - volatile struct input_shared_memory *shared = get_foreground_shared_memory(); - HWND ret = 0; - - if (!shared) return 0; - - SHARED_READ_BEGIN( &shared->seq ) - { - ret = wine_server_ptr_handle( shared->active ); - } - SHARED_READ_END( &shared->seq ); - - return ret; -} - -/* see GetActiveWindow */ -HWND get_active_window(void) -{ - GUITHREADINFO info; - info.cbSize = sizeof(info); - return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0; -} - -/* see GetCapture */ -HWND get_capture(void) -{ - GUITHREADINFO info; - info.cbSize = sizeof(info); - return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0; -} - -/* see GetFocus */ -HWND get_focus(void) -{ - GUITHREADINFO info; - info.cbSize = sizeof(info); - return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0; -} - /***************************************************************** * set_focus_window * From 793ac525e3dbdad9b62500139e457715559be0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 13:39:26 +0100 Subject: [PATCH 082/758] win32u: Send WM_INPUTLANGCHANGE when activating new layout. (cherry picked from commit fee5eaecb47aca10cd17d5f84e2bbc3ca60bfa44) --- dlls/win32u/input.c | 18 +++++++++++++++++- dlls/win32u/ntgdi_private.h | 1 - dlls/win32u/win32u_private.h | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 9575a0a1172..73b50f6d656 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1195,6 +1195,7 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) { struct user_thread_info *info = get_user_thread_info(); HKL old_layout; + HWND focus; TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags ); @@ -1212,7 +1213,22 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) old_layout = info->kbd_layout; info->kbd_layout = layout; - if (old_layout != layout) info->kbd_layout_id = 0; + if (old_layout != layout) + { + const NLS_LOCALE_DATA *data; + CHARSETINFO cs = {0}; + + if (HIWORD(layout) & 0x8000) + FIXME( "Aliased keyboard layout not yet implemented\n" ); + else if (!(data = get_locale_data( HIWORD(layout) ))) + WARN( "Failed to find locale data for %04x\n", HIWORD(layout) ); + else + translate_charset_info( ULongToPtr(data->idefaultansicodepage), &cs, TCI_SRCCODEPAGE ); + + info->kbd_layout_id = 0; + if ((focus = get_focus()) && get_window_thread( focus, NULL ) == GetCurrentThreadId()) + send_message( focus, WM_INPUTLANGCHANGE, cs.ciCharset, (LPARAM)layout ); + } if (!old_layout) return get_locale_kbd_layout(); return old_layout; diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index ce5e30ab07d..93be59c9d7e 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -368,7 +368,6 @@ extern BOOL opentype_enum_full_names( const struct tt_name_v0 *tt_name_v0, extern BOOL opentype_get_properties( const void *data, size_t size, const struct ttc_sfnt_v1 *ttc_sfnt_v1, DWORD *version, FONTSIGNATURE *fs, DWORD *ntm_flags ) DECLSPEC_HIDDEN; -extern BOOL translate_charset_info( DWORD *src, CHARSETINFO *cs, DWORD flags ) DECLSPEC_HIDDEN; /* gdiobj.c */ extern HGDIOBJ alloc_gdi_handle( struct gdi_obj_header *obj, DWORD type, diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index bba36a43ae0..a885128d3a7 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -458,6 +458,7 @@ extern CPTABLEINFO ansi_cp DECLSPEC_HIDDEN; CPTABLEINFO *get_cptable( WORD cp ) DECLSPEC_HIDDEN; const NLS_LOCALE_DATA *get_locale_data( LCID lcid ) DECLSPEC_HIDDEN; +extern BOOL translate_charset_info( DWORD *src, CHARSETINFO *cs, DWORD flags ) DECLSPEC_HIDDEN; DWORD win32u_mbtowc( CPTABLEINFO *info, WCHAR *dst, DWORD dstlen, const char *src, DWORD srclen ) DECLSPEC_HIDDEN; DWORD win32u_wctomb( CPTABLEINFO *info, char *dst, DWORD dstlen, const WCHAR *src, From 7fa24be043c7d01b8a43ccab09f0f86a11585600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 14:44:55 +0100 Subject: [PATCH 083/758] imm32: Implement stubs for ImmFreeLayout and ImmLoadIME. (cherry picked from commit 89cdae2e515495fe4f9fcdb90b366e29e5df2b8c) --- dlls/imm32/imm.c | 12 ++++++++++++ dlls/imm32/imm32.spec | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 3d9cbf7e198..f9aa6e350c8 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -478,6 +478,18 @@ static HMODULE load_graphics_driver(void) return ret; } +BOOL WINAPI ImmFreeLayout( HKL hkl ) +{ + FIXME( "hkl %p stub!\n", hkl ); + return FALSE; +} + +BOOL WINAPI ImmLoadIME( HKL hkl ) +{ + FIXME( "hkl %p stub!\n", hkl ); + return FALSE; +} + /* ImmHkl loading and freeing */ #define LOAD_FUNCPTR(f) if((ptr->p##f = (LPVOID)GetProcAddress(ptr->hIME, #f)) == NULL){WARN("Can't find function %s in ime\n", #f);} static ImmHkl *IMM_GetImmHkl(HKL hkl) diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 70b8aef3a95..9c7ce13319f 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -18,7 +18,7 @@ @ stdcall ImmEnumRegisterWordW(long ptr wstr long wstr ptr) @ stdcall ImmEscapeA(long long long ptr) @ stdcall ImmEscapeW(long long long ptr) -@ stub ImmFreeLayout +@ stdcall ImmFreeLayout(long) @ stdcall ImmGenerateMessage(ptr) @ stdcall ImmGetCandidateListA(long long ptr long) @ stdcall ImmGetCandidateListCountA(long ptr) @@ -66,7 +66,7 @@ @ stdcall ImmIsIME(long) @ stdcall ImmIsUIMessageA(long long long long) @ stdcall ImmIsUIMessageW(long long long long) -@ stub ImmLoadIME +@ stdcall ImmLoadIME(long) @ stub ImmLoadLayout @ stub ImmLockClientImc @ stdcall ImmLockIMC(long) From a54b8b2e179fe969023404b14befbca2207155eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 19:19:06 +0100 Subject: [PATCH 084/758] imm32/tests: Test undocumented ImmLoadIME / ImmFreeLayout. (cherry picked from commit 05cf38bfa63980af7a094b1c087bee4ea36eb485) --- dlls/imm32/tests/imm32.c | 139 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 97f3a5394ea..9d63c80c514 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -42,6 +42,9 @@ static UINT (WINAPI *pNtUserAssociateInputContext)(HWND,HIMC,ULONG); static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM); static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t); +extern BOOL WINAPI ImmFreeLayout(HKL); +extern BOOL WINAPI ImmLoadIME(HKL); + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE @@ -2422,6 +2425,13 @@ static void test_ImmDisableIME(void) #define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) +DEFINE_EXPECT( ImeInquire ); +DEFINE_EXPECT( ImeDestroy ); +DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); +DEFINE_EXPECT( IME_DLL_PROCESS_DETACH ); + +static IMEINFO ime_info; + static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); @@ -2457,7 +2467,11 @@ static DWORD WINAPI ime_ImeConversionList( HIMC himc, const WCHAR *source, CANDI static BOOL WINAPI ime_ImeDestroy( UINT force ) { ime_trace( "force %u\n", force ); - ok( 0, "unexpected call\n" ); + + CHECK_EXPECT( ImeDestroy ); + + ok( !force, "got force %u\n", force ); + return TRUE; } @@ -2496,7 +2510,21 @@ static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style ) static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) { ime_trace( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags ); - ok( 0, "unexpected call\n" ); + + CHECK_EXPECT( ImeInquire ); + + ok( !!info, "got info %p\n", info ); + ok( !!ui_class, "got ui_class %p\n", ui_class ); + ok( !flags, "got flags %#lx\n", flags ); + + *info = ime_info; + + if (ime_info.fdwProperty & IME_PROP_UNICODE) + wcscpy( ui_class, ime_ui_class.lpszClassName ); + else + WideCharToMultiByte( CP_ACP, 0, ime_ui_class.lpszClassName, -1, + (char *)ui_class, 17, NULL, NULL ); + return TRUE; } @@ -2570,10 +2598,12 @@ static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserve DisableThreadLibraryCalls( instance ); ime_ui_class.hInstance = instance; RegisterClassExW( &ime_ui_class ); + CHECK_EXPECT( IME_DLL_PROCESS_ATTACH ); break; case DLL_PROCESS_DETACH: UnregisterClassW( ime_ui_class.lpszClassName, instance ); + todo_wine CHECK_EXPECT( IME_DLL_PROCESS_DETACH ); break; } @@ -2724,11 +2754,88 @@ static void ime_cleanup( HKL hkl ) static void test_ImmInstallIME(void) { + UINT ret; HKL hkl; - if (!(hkl = ime_install())) return; + SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE ); + SET_ENABLE( ImeInquire, TRUE ); + SET_ENABLE( ImeDestroy, TRUE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + + if (!(hkl = ime_install())) goto cleanup; + + SET_EXPECT( IME_DLL_PROCESS_ATTACH ); + SET_EXPECT( ImeInquire ); + ret = ImmLoadIME( hkl ); + todo_wine + ok( ret, "ImmLoadIME returned %#x\n", ret ); + todo_wine + CHECK_CALLED( IME_DLL_PROCESS_ATTACH ); + todo_wine + CHECK_CALLED( ImeInquire ); + + ret = ImmLoadIME( hkl ); + todo_wine + ok( ret, "ImmLoadIME returned %#x\n", ret ); + + SET_EXPECT( ImeDestroy ); + SET_EXPECT( IME_DLL_PROCESS_DETACH ); + ret = ImmFreeLayout( hkl ); + todo_wine + ok( ret, "ImmFreeLayout returned %#x\n", ret ); + todo_wine + CHECK_CALLED( ImeDestroy ); + todo_wine + CHECK_CALLED( IME_DLL_PROCESS_DETACH ); + + ret = ImmFreeLayout( hkl ); + todo_wine + ok( ret, "ImmFreeLayout returned %#x\n", ret ); + + ime_cleanup( hkl ); + + ime_info.fdwProperty = 0; + + if (!(hkl = ime_install())) goto cleanup; + + SET_EXPECT( IME_DLL_PROCESS_ATTACH ); + SET_EXPECT( ImeInquire ); + ret = ImmLoadIME( hkl ); + todo_wine + ok( ret, "ImmLoadIME returned %#x\n", ret ); + todo_wine + CHECK_CALLED( IME_DLL_PROCESS_ATTACH ); + todo_wine + CHECK_CALLED( ImeInquire ); + + ret = ImmLoadIME( hkl ); + todo_wine + ok( ret, "ImmLoadIME returned %#x\n", ret ); + + SET_EXPECT( ImeDestroy ); + SET_EXPECT( IME_DLL_PROCESS_DETACH ); + ret = ImmFreeLayout( hkl ); + todo_wine + ok( ret, "ImmFreeLayout returned %#x\n", ret ); + todo_wine + CHECK_CALLED( ImeDestroy ); + todo_wine + CHECK_CALLED( IME_DLL_PROCESS_DETACH ); + + ret = ImmFreeLayout( hkl ); + todo_wine + ok( ret, "ImmFreeLayout returned %#x\n", ret ); ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); + SET_ENABLE( ImeInquire, FALSE ); + SET_ENABLE( ImeDestroy, FALSE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); } static void test_ImmGetDescription(void) @@ -2738,6 +2845,11 @@ static void test_ImmGetDescription(void) char bufferA[MAX_PATH]; DWORD ret; + SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE ); + SET_ENABLE( ImeInquire, TRUE ); + SET_ENABLE( ImeDestroy, TRUE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE ); + SetLastError( 0xdeadbeef ); ret = ImmGetDescriptionW( NULL, NULL, 0 ); ok( !ret, "ImmGetDescriptionW returned %lu\n", ret ); @@ -2756,7 +2868,7 @@ static void test_ImmGetDescription(void) ret = GetLastError(); ok( ret == 0xdeadbeef, "got error %lu\n", ret ); - if (!(hkl = ime_install())) return; + if (!(hkl = ime_install())) goto cleanup; memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetDescriptionW( hkl, bufferW, 2 ); @@ -2808,6 +2920,12 @@ static void test_ImmGetDescription(void) ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); + SET_ENABLE( ImeInquire, FALSE ); + SET_ENABLE( ImeDestroy, FALSE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); } static void test_ImmGetIMEFileName(void) @@ -2817,6 +2935,11 @@ static void test_ImmGetIMEFileName(void) char bufferA[MAX_PATH], expectA[16]; DWORD ret; + SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE ); + SET_ENABLE( ImeInquire, TRUE ); + SET_ENABLE( ImeDestroy, TRUE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE ); + SetLastError( 0xdeadbeef ); ret = ImmGetIMEFileNameW( NULL, NULL, 0 ); ok( !ret, "ImmGetIMEFileNameW returned %lu\n", ret ); @@ -2834,7 +2957,7 @@ static void test_ImmGetIMEFileName(void) todo_wine ok( ret == 0xdeadbeef, "got error %lu\n", ret ); - if (!(hkl = ime_install())) return; + if (!(hkl = ime_install())) goto cleanup; memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetIMEFileNameW( hkl, bufferW, 2 ); @@ -2891,6 +3014,12 @@ static void test_ImmGetIMEFileName(void) ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); + SET_ENABLE( ImeInquire, FALSE ); + SET_ENABLE( ImeDestroy, FALSE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); } START_TEST(imm32) From f08852ffaf1eab811985e88c52b173c8ba47ac88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 11:27:38 +0100 Subject: [PATCH 085/758] imm32: Rename ImmHkl to struct ime. (cherry picked from commit 8f12fac6816da14b681e6bc3edcdc0994e2a8aa0) --- dlls/imm32/imm.c | 59 ++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index f9aa6e350c8..403ff17ad5c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -52,7 +52,8 @@ static UINT WM_MSIME_RECONVERT; static UINT WM_MSIME_QUERYPOSITION; static UINT WM_MSIME_DOCUMENTFEED; -typedef struct _tagImmHkl{ +struct ime +{ struct list entry; HKL hkl; HMODULE hIME; @@ -78,7 +79,7 @@ typedef struct _tagImmHkl{ BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *); UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *); DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD); -} ImmHkl; +}; static HRESULT (WINAPI *pCoRevokeInitializeSpy)(ULARGE_INTEGER cookie); static void (WINAPI *pCoUninitialize)(void); @@ -90,7 +91,7 @@ typedef struct tagInputContextData INPUTCONTEXT IMC; DWORD threadID; - ImmHkl *immKbd; + struct ime *immKbd; UINT lastVK; BOOL threadDefault; } InputContextData; @@ -120,7 +121,7 @@ static inline BOOL is_himc_ime_unicode(const InputContextData *data) return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE); } -static inline BOOL is_kbd_ime_unicode(const ImmHkl *hkl) +static inline BOOL is_kbd_ime_unicode( const struct ime *hkl ) { return !!(hkl->imeInfo.fdwProperty & IME_PROP_UNICODE); } @@ -490,23 +491,23 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) return FALSE; } -/* ImmHkl loading and freeing */ +/* struct ime loading and freeing */ #define LOAD_FUNCPTR(f) if((ptr->p##f = (LPVOID)GetProcAddress(ptr->hIME, #f)) == NULL){WARN("Can't find function %s in ime\n", #f);} -static ImmHkl *IMM_GetImmHkl(HKL hkl) +static struct ime *IMM_GetImmHkl( HKL hkl ) { - ImmHkl *ptr; + struct ime *ptr; WCHAR filename[MAX_PATH]; TRACE("Seeking ime for keyboard %p\n",hkl); - LIST_FOR_EACH_ENTRY(ptr, &ImmHklList, ImmHkl, entry) + LIST_FOR_EACH_ENTRY( ptr, &ImmHklList, struct ime, entry ) { if (ptr->hkl == hkl) return ptr; } /* not found... create it */ - ptr = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ImmHkl)); + ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ime) ); ptr->hkl = hkl; if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename); @@ -563,9 +564,9 @@ static ImmHkl *IMM_GetImmHkl(HKL hkl) static void IMM_FreeAllImmHkl(void) { - ImmHkl *ptr,*cursor2; + struct ime *ptr, *cursor2; - LIST_FOR_EACH_ENTRY_SAFE(ptr, cursor2, &ImmHklList, ImmHkl, entry) + LIST_FOR_EACH_ENTRY_SAFE( ptr, cursor2, &ImmHklList, struct ime, entry ) { list_remove(&ptr->entry); if (ptr->hIME) @@ -762,7 +763,7 @@ BOOL WINAPI ImmAssociateContextEx(HWND hwnd, HIMC imc, DWORD flags) BOOL WINAPI ImmConfigureIMEA( HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %ld, %p):\n", hKL, hWnd, dwMode, lpData); @@ -797,7 +798,7 @@ BOOL WINAPI ImmConfigureIMEA( BOOL WINAPI ImmConfigureIMEW( HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %ld, %p):\n", hKL, hWnd, dwMode, lpData); @@ -938,7 +939,7 @@ UINT WINAPI ImmEnumRegisterWordA( LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszRegister, LPVOID lpData) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %s, %ld, %s, %p):\n", hKL, lpfnEnumProc, debugstr_a(lpszReading), dwStyle, debugstr_a(lpszRegister), lpData); if (immHkl->hIME && immHkl->pImeEnumRegisterWord) @@ -973,7 +974,7 @@ UINT WINAPI ImmEnumRegisterWordW( LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister, LPVOID lpData) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %s, %ld, %s, %p):\n", hKL, lpfnEnumProc, debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister), lpData); if (immHkl->hIME && immHkl->pImeEnumRegisterWord) @@ -1016,7 +1017,7 @@ LRESULT WINAPI ImmEscapeA( HKL hKL, HIMC hIMC, UINT uEscape, LPVOID lpData) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData); if (immHkl->hIME && immHkl->pImeEscape) @@ -1051,7 +1052,7 @@ LRESULT WINAPI ImmEscapeW( HKL hKL, HIMC hIMC, UINT uEscape, LPVOID lpData) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData); if (immHkl->hIME && immHkl->pImeEscape) @@ -1620,7 +1621,7 @@ DWORD WINAPI ImmGetConversionListA( LPCSTR pSrc, LPCANDIDATELIST lpDst, DWORD dwBufLen, UINT uFlag) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %s, %p, %ld, %d):\n", hKL, hIMC, debugstr_a(pSrc), lpDst, dwBufLen, uFlag); if (immHkl->hIME && immHkl->pImeConversionList) @@ -1658,7 +1659,7 @@ DWORD WINAPI ImmGetConversionListW( LPCWSTR pSrc, LPCANDIDATELIST lpDst, DWORD dwBufLen, UINT uFlag) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %p, %s, %p, %ld, %d):\n", hKL, hIMC, debugstr_w(pSrc), lpDst, dwBufLen, uFlag); if (immHkl->hIME && immHkl->pImeConversionList) @@ -1895,7 +1896,7 @@ BOOL WINAPI ImmGetOpenStatus(HIMC hIMC) DWORD WINAPI ImmGetProperty(HKL hKL, DWORD fdwIndex) { DWORD rc = 0; - ImmHkl *kbd; + struct ime *kbd; TRACE("(%p, %ld)\n", hKL, fdwIndex); kbd = IMM_GetImmHkl(hKL); @@ -1923,7 +1924,7 @@ DWORD WINAPI ImmGetProperty(HKL hKL, DWORD fdwIndex) UINT WINAPI ImmGetRegisterWordStyleA( HKL hKL, UINT nItem, LPSTYLEBUFA lpStyleBuf) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %d, %p):\n", hKL, nItem, lpStyleBuf); if (immHkl->hIME && immHkl->pImeGetRegisterWordStyle) { @@ -1951,7 +1952,7 @@ UINT WINAPI ImmGetRegisterWordStyleA( UINT WINAPI ImmGetRegisterWordStyleW( HKL hKL, UINT nItem, LPSTYLEBUFW lpStyleBuf) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %d, %p):\n", hKL, nItem, lpStyleBuf); if (immHkl->hIME && immHkl->pImeGetRegisterWordStyle) { @@ -2102,7 +2103,7 @@ HKL WINAPI ImmInstallIMEW( */ BOOL WINAPI ImmIsIME(HKL hKL) { - ImmHkl *ptr; + struct ime *ptr; TRACE("(%p):\n", hKL); ptr = IMM_GetImmHkl(hKL); return (ptr && ptr->hIME); @@ -2183,7 +2184,7 @@ BOOL WINAPI ImmNotifyIME( BOOL WINAPI ImmRegisterWordA( HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszRegister) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_a(lpszReading), dwStyle, debugstr_a(lpszRegister)); if (immHkl->hIME && immHkl->pImeRegisterWord) @@ -2213,7 +2214,7 @@ BOOL WINAPI ImmRegisterWordA( BOOL WINAPI ImmRegisterWordW( HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister)); if (immHkl->hIME && immHkl->pImeRegisterWord) @@ -2673,7 +2674,7 @@ BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID) BOOL WINAPI ImmUnregisterWordA( HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszUnregister) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_a(lpszReading), dwStyle, debugstr_a(lpszUnregister)); if (immHkl->hIME && immHkl->pImeUnregisterWord) @@ -2703,7 +2704,7 @@ BOOL WINAPI ImmUnregisterWordA( BOOL WINAPI ImmUnregisterWordW( HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister) { - ImmHkl *immHkl = IMM_GetImmHkl(hKL); + struct ime *immHkl = IMM_GetImmHkl( hKL ); TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_w(lpszReading), dwStyle, debugstr_w(lpszUnregister)); if (immHkl->hIME && immHkl->pImeUnregisterWord) @@ -3078,7 +3079,7 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD /* Make sure we are inputting to the correct keyboard */ if (data->immKbd->hkl != hKL) { - ImmHkl *new_hkl = IMM_GetImmHkl(hKL); + struct ime *new_hkl = IMM_GetImmHkl( hKL ); if (new_hkl) { data->immKbd->pImeSelect(imc, FALSE); @@ -3145,7 +3146,7 @@ BOOL WINAPI ImmDisableLegacyIME(void) static HWND get_ui_window(HKL hkl) { - ImmHkl *immHkl = IMM_GetImmHkl(hkl); + struct ime *immHkl = IMM_GetImmHkl( hkl ); return immHkl->UIWnd; } From 691a9821ed1200d75fe1aeb59ff53acace1f35fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 086/758] imm32: Reorder control flow in ImmConfigureIMEA. (cherry picked from commit e0229ba326ffb67c108845aae4b6b1df48ac4917) --- dlls/imm32/imm.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 403ff17ad5c..64351a8a787 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -760,36 +760,30 @@ BOOL WINAPI ImmAssociateContextEx(HWND hwnd, HIMC imc, DWORD flags) /*********************************************************************** * ImmConfigureIMEA (IMM32.@) */ -BOOL WINAPI ImmConfigureIMEA( - HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) +BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); + struct ime *ime = IMM_GetImmHkl( hkl ); + BOOL ret; - TRACE("(%p, %p, %ld, %p):\n", hKL, hWnd, dwMode, lpData); + TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); - if (dwMode == IME_CONFIG_REGISTERWORD && !lpData) - return FALSE; + if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; + if (!ime->hIME || !ime->pImeConfigure) return FALSE; - if (immHkl->hIME && immHkl->pImeConfigure) + if (mode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode( ime )) + ret = ime->pImeConfigure( hkl, hwnd, mode, data ); + else { - if (dwMode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode(immHkl)) - return immHkl->pImeConfigure(hKL,hWnd,dwMode,lpData); - else - { - REGISTERWORDW rww; - REGISTERWORDA *rwa = lpData; - BOOL rc; - - rww.lpReading = strdupAtoW(rwa->lpReading); - rww.lpWord = strdupAtoW(rwa->lpWord); - rc = immHkl->pImeConfigure(hKL,hWnd,dwMode,&rww); - HeapFree(GetProcessHeap(),0,rww.lpReading); - HeapFree(GetProcessHeap(),0,rww.lpWord); - return rc; - } + REGISTERWORDA *wordA = data; + REGISTERWORDW wordW; + wordW.lpWord = strdupAtoW( wordA->lpWord ); + wordW.lpReading = strdupAtoW( wordA->lpReading ); + ret = ime->pImeConfigure( hkl, hwnd, mode, &wordW ); + HeapFree( GetProcessHeap(), 0, wordW.lpReading ); + HeapFree( GetProcessHeap(), 0, wordW.lpWord ); } - else - return FALSE; + + return ret; } /*********************************************************************** From 255608e005848b41143d6156b4c239599318655a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 087/758] imm32: Reorder control flow in ImmConfigureIMEW. (cherry picked from commit 38152f893d4a6dd1a7b0e627ff26635d76ee172e) --- dlls/imm32/imm.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 64351a8a787..8f1d2822ac9 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -789,36 +789,30 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) /*********************************************************************** * ImmConfigureIMEW (IMM32.@) */ -BOOL WINAPI ImmConfigureIMEW( - HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) +BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); + struct ime *ime = IMM_GetImmHkl( hkl ); + BOOL ret; - TRACE("(%p, %p, %ld, %p):\n", hKL, hWnd, dwMode, lpData); + TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); - if (dwMode == IME_CONFIG_REGISTERWORD && !lpData) - return FALSE; + if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; + if (!ime->hIME || !ime->pImeConfigure) return FALSE; - if (immHkl->hIME && immHkl->pImeConfigure) + if (mode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode( ime )) + ret = ime->pImeConfigure( hkl, hwnd, mode, data ); + else { - if (dwMode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode(immHkl)) - return immHkl->pImeConfigure(hKL,hWnd,dwMode,lpData); - else - { - REGISTERWORDW *rww = lpData; - REGISTERWORDA rwa; - BOOL rc; - - rwa.lpReading = strdupWtoA(rww->lpReading); - rwa.lpWord = strdupWtoA(rww->lpWord); - rc = immHkl->pImeConfigure(hKL,hWnd,dwMode,&rwa); - HeapFree(GetProcessHeap(),0,rwa.lpReading); - HeapFree(GetProcessHeap(),0,rwa.lpWord); - return rc; - } + REGISTERWORDW *wordW = data; + REGISTERWORDA wordA; + wordA.lpWord = strdupWtoA( wordW->lpWord ); + wordA.lpReading = strdupWtoA( wordW->lpReading ); + ret = ime->pImeConfigure( hkl, hwnd, mode, &wordA ); + HeapFree( GetProcessHeap(), 0, wordA.lpReading ); + HeapFree( GetProcessHeap(), 0, wordA.lpWord ); } - else - return FALSE; + + return ret; } static InputContextData *create_input_context(HIMC default_imc) From 88eaa822997b2319dea165becb9886f899de4bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 088/758] imm32: Reorder control flow in ImmEnumRegisterWordA. (cherry picked from commit 93938a83df1bd33a3849df1e1c060c053d431643) --- dlls/imm32/imm.c | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 8f1d2822ac9..942088bd613 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -922,36 +922,29 @@ BOOL WINAPI ImmDestroyContext(HIMC hIMC) /*********************************************************************** * ImmEnumRegisterWordA (IMM32.@) */ -UINT WINAPI ImmEnumRegisterWordA( - HKL hKL, REGISTERWORDENUMPROCA lpfnEnumProc, - LPCSTR lpszReading, DWORD dwStyle, - LPCSTR lpszRegister, LPVOID lpData) +UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const char *readingA, + DWORD style, const char *stringA, void *user ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %p, %s, %ld, %s, %p):\n", hKL, lpfnEnumProc, - debugstr_a(lpszReading), dwStyle, debugstr_a(lpszRegister), lpData); - if (immHkl->hIME && immHkl->pImeEnumRegisterWord) - { - if (!is_kbd_ime_unicode(immHkl)) - return immHkl->pImeEnumRegisterWord((REGISTERWORDENUMPROCW)lpfnEnumProc, - (LPCWSTR)lpszReading, dwStyle, (LPCWSTR)lpszRegister, lpData); - else - { - LPWSTR lpszwReading = strdupAtoW(lpszReading); - LPWSTR lpszwRegister = strdupAtoW(lpszRegister); - BOOL rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + UINT ret; - rc = immHkl->pImeEnumRegisterWord((REGISTERWORDENUMPROCW)lpfnEnumProc, - lpszwReading, dwStyle, lpszwRegister, - lpData); + TRACE( "hkl %p, procA %p, readingA %s, style %lu, stringA %s, user %p.\n", hkl, procA, + debugstr_a(readingA), style, debugstr_a(stringA), user ); - HeapFree(GetProcessHeap(),0,lpszwReading); - HeapFree(GetProcessHeap(),0,lpszwRegister); - return rc; - } - } + if (!ime->hIME || !ime->pImeEnumRegisterWord) return 0; + + if (!is_kbd_ime_unicode( ime )) + ret = ime->pImeEnumRegisterWord( (REGISTERWORDENUMPROCW)procA, (const WCHAR *)readingA, + style, (const WCHAR *)stringA, user ); else - return 0; + { + WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); + ret = ime->pImeEnumRegisterWord( (REGISTERWORDENUMPROCW)procA, readingW, style, stringW, user ); + HeapFree( GetProcessHeap(), 0, readingW ); + HeapFree( GetProcessHeap(), 0, stringW ); + } + + return ret; } /*********************************************************************** From eeac68149dae5d8c8a08a5a10cd637b5d6a1b65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 089/758] imm32: Reorder control flow in ImmEnumRegisterWordW. (cherry picked from commit c358707a4d3abf1c92b05e473dace2a3f800341b) --- dlls/imm32/imm.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 942088bd613..448781e7815 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -950,35 +950,29 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch /*********************************************************************** * ImmEnumRegisterWordW (IMM32.@) */ -UINT WINAPI ImmEnumRegisterWordW( - HKL hKL, REGISTERWORDENUMPROCW lpfnEnumProc, - LPCWSTR lpszReading, DWORD dwStyle, - LPCWSTR lpszRegister, LPVOID lpData) +UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WCHAR *readingW, + DWORD style, const WCHAR *stringW, void *user ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %p, %s, %ld, %s, %p):\n", hKL, lpfnEnumProc, - debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister), lpData); - if (immHkl->hIME && immHkl->pImeEnumRegisterWord) - { - if (is_kbd_ime_unicode(immHkl)) - return immHkl->pImeEnumRegisterWord(lpfnEnumProc, lpszReading, dwStyle, - lpszRegister, lpData); - else - { - LPSTR lpszaReading = strdupWtoA(lpszReading); - LPSTR lpszaRegister = strdupWtoA(lpszRegister); - BOOL rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + UINT ret; - rc = immHkl->pImeEnumRegisterWord(lpfnEnumProc, (LPCWSTR)lpszaReading, - dwStyle, (LPCWSTR)lpszaRegister, lpData); + TRACE( "hkl %p, procW %p, readingW %s, style %lu, stringW %s, user %p.\n", hkl, procW, + debugstr_w(readingW), style, debugstr_w(stringW), user ); - HeapFree(GetProcessHeap(),0,lpszaReading); - HeapFree(GetProcessHeap(),0,lpszaRegister); - return rc; - } - } + if (!ime->hIME || !ime->pImeEnumRegisterWord) return 0; + + if (is_kbd_ime_unicode( ime )) + ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user ); else - return 0; + { + char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); + ret = ime->pImeEnumRegisterWord( procW, (const WCHAR *)readingA, style, + (const WCHAR *)stringA, user ); + HeapFree( GetProcessHeap(), 0, readingA ); + HeapFree( GetProcessHeap(), 0, stringA ); + } + + return ret; } static inline BOOL EscapeRequiresWA(UINT uEscape) From 37d24dfff795f9085a9a7a15446bd8d69fc6d6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 090/758] imm32: Reorder control flow in ImmEscapeA. (cherry picked from commit f5c5839bd316438e7f2d59752ab82a83d7d913f1) --- dlls/imm32/imm.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 448781e7815..b87ff211698 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -988,36 +988,33 @@ static inline BOOL EscapeRequiresWA(UINT uEscape) /*********************************************************************** * ImmEscapeA (IMM32.@) */ -LRESULT WINAPI ImmEscapeA( - HKL hKL, HIMC hIMC, - UINT uEscape, LPVOID lpData) +LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData); + struct ime *ime = IMM_GetImmHkl( hkl ); + LRESULT ret; - if (immHkl->hIME && immHkl->pImeEscape) + TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); + + if (!ime->hIME || !ime->pImeEscape) return 0; + + if (!EscapeRequiresWA( code ) || !is_kbd_ime_unicode( ime )) + ret = ime->pImeEscape( himc, code, data ); + else { - if (!EscapeRequiresWA(uEscape) || !is_kbd_ime_unicode(immHkl)) - return immHkl->pImeEscape(hIMC,uEscape,lpData); + WCHAR buffer[81]; /* largest required buffer should be 80 */ + if (code == IME_ESC_SET_EUDC_DICTIONARY) + { + MultiByteToWideChar( CP_ACP, 0, data, -1, buffer, 81 ); + ret = ime->pImeEscape( himc, code, buffer ); + } else { - WCHAR buffer[81]; /* largest required buffer should be 80 */ - LRESULT rc; - if (uEscape == IME_ESC_SET_EUDC_DICTIONARY) - { - MultiByteToWideChar(CP_ACP,0,lpData,-1,buffer,81); - rc = immHkl->pImeEscape(hIMC,uEscape,buffer); - } - else - { - rc = immHkl->pImeEscape(hIMC,uEscape,buffer); - WideCharToMultiByte(CP_ACP,0,buffer,-1,lpData,80, NULL, NULL); - } - return rc; + ret = ime->pImeEscape( himc, code, buffer ); + WideCharToMultiByte( CP_ACP, 0, buffer, -1, data, 80, NULL, NULL ); } } - else - return 0; + + return ret; } /*********************************************************************** From 5f940fed2777dbe430a92f3a34c7207dfd4a94fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 091/758] imm32: Reorder control flow in ImmEscapeW. (cherry picked from commit af926f1bdcb952823754cdbd57c60b2943efeb97) --- dlls/imm32/imm.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index b87ff211698..6fab7c9981b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1020,36 +1020,33 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) /*********************************************************************** * ImmEscapeW (IMM32.@) */ -LRESULT WINAPI ImmEscapeW( - HKL hKL, HIMC hIMC, - UINT uEscape, LPVOID lpData) +LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData); + struct ime *ime = IMM_GetImmHkl( hkl ); + LRESULT ret; - if (immHkl->hIME && immHkl->pImeEscape) + TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); + + if (!ime->hIME || !ime->pImeEscape) return 0; + + if (!EscapeRequiresWA( code ) || is_kbd_ime_unicode( ime )) + ret = ime->pImeEscape( himc, code, data ); + else { - if (!EscapeRequiresWA(uEscape) || is_kbd_ime_unicode(immHkl)) - return immHkl->pImeEscape(hIMC,uEscape,lpData); + char buffer[81]; /* largest required buffer should be 80 */ + if (code == IME_ESC_SET_EUDC_DICTIONARY) + { + WideCharToMultiByte( CP_ACP, 0, data, -1, buffer, 81, NULL, NULL ); + ret = ime->pImeEscape( himc, code, buffer ); + } else { - CHAR buffer[81]; /* largest required buffer should be 80 */ - LRESULT rc; - if (uEscape == IME_ESC_SET_EUDC_DICTIONARY) - { - WideCharToMultiByte(CP_ACP,0,lpData,-1,buffer,81, NULL, NULL); - rc = immHkl->pImeEscape(hIMC,uEscape,buffer); - } - else - { - rc = immHkl->pImeEscape(hIMC,uEscape,buffer); - MultiByteToWideChar(CP_ACP,0,buffer,-1,lpData,80); - } - return rc; + ret = ime->pImeEscape( himc, code, buffer ); + MultiByteToWideChar( CP_ACP, 0, buffer, -1, data, 80 ); } } - else - return 0; + + return ret; } /*********************************************************************** From 8b4bd414bb5ac87f9fbf40509ddb3c40ccd45a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 092/758] imm32: Reorder control flow in ImmGetConversionListA. (cherry picked from commit 27b587d967d0556b18265a7f6b6352ca0a1bfda4) --- dlls/imm32/imm.c | 51 +++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 6fab7c9981b..7946b54d689 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1585,39 +1585,36 @@ HIMC WINAPI ImmGetContext(HWND hWnd) /*********************************************************************** * ImmGetConversionListA (IMM32.@) */ -DWORD WINAPI ImmGetConversionListA( - HKL hKL, HIMC hIMC, - LPCSTR pSrc, LPCANDIDATELIST lpDst, - DWORD dwBufLen, UINT uFlag) +DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDIDATELIST *listA, + DWORD lengthA, UINT flags ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %p, %s, %p, %ld, %d):\n", hKL, hIMC, debugstr_a(pSrc), lpDst, - dwBufLen, uFlag); - if (immHkl->hIME && immHkl->pImeConversionList) + struct ime *ime = IMM_GetImmHkl( hkl ); + DWORD ret; + + TRACE( "hkl %p, himc %p, srcA %s, listA %p, lengthA %lu, flags %#x.\n", hkl, himc, + debugstr_a(srcA), listA, lengthA, flags ); + + if (!ime->hIME || !ime->pImeConversionList) return 0; + + if (!is_kbd_ime_unicode( ime )) + ret = ime->pImeConversionList( himc, (const WCHAR *)srcA, listA, lengthA, flags ); + else { - if (!is_kbd_ime_unicode(immHkl)) - return immHkl->pImeConversionList(hIMC,(LPCWSTR)pSrc,lpDst,dwBufLen,uFlag); + CANDIDATELIST *listW; + WCHAR *srcW = strdupAtoW( srcA ); + DWORD lengthW = ime->pImeConversionList( himc, srcW, NULL, 0, flags ); + + if (!(listW = HeapAlloc( GetProcessHeap(), 0, lengthW ))) ret = 0; else { - LPCANDIDATELIST lpwDst; - DWORD ret = 0, len; - LPWSTR pwSrc = strdupAtoW(pSrc); - - len = immHkl->pImeConversionList(hIMC, pwSrc, NULL, 0, uFlag); - lpwDst = HeapAlloc(GetProcessHeap(), 0, len); - if ( lpwDst ) - { - immHkl->pImeConversionList(hIMC, pwSrc, lpwDst, len, uFlag); - ret = convert_candidatelist_WtoA( lpwDst, lpDst, dwBufLen); - HeapFree(GetProcessHeap(), 0, lpwDst); - } - HeapFree(GetProcessHeap(), 0, pwSrc); - - return ret; + ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); + ret = convert_candidatelist_WtoA( listW, listA, lengthA ); + HeapFree( GetProcessHeap(), 0, listW ); } + HeapFree( GetProcessHeap(), 0, srcW ); } - else - return 0; + + return ret; } /*********************************************************************** From 12db4883d15a1b8d9e803ee115da675e2b9bb400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 093/758] imm32: Reorder control flow in ImmGetConversionListW. (cherry picked from commit a8f11bb8f348c38f805bb3f85c3f88f0d86cd136) --- dlls/imm32/imm.c | 51 +++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 7946b54d689..6a5ee4f2217 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1620,39 +1620,36 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID /*********************************************************************** * ImmGetConversionListW (IMM32.@) */ -DWORD WINAPI ImmGetConversionListW( - HKL hKL, HIMC hIMC, - LPCWSTR pSrc, LPCANDIDATELIST lpDst, - DWORD dwBufLen, UINT uFlag) +DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDIDATELIST *listW, + DWORD lengthW, UINT flags ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %p, %s, %p, %ld, %d):\n", hKL, hIMC, debugstr_w(pSrc), lpDst, - dwBufLen, uFlag); - if (immHkl->hIME && immHkl->pImeConversionList) + struct ime *ime = IMM_GetImmHkl( hkl ); + DWORD ret; + + TRACE( "hkl %p, himc %p, srcW %s, listW %p, lengthW %lu, flags %#x.\n", hkl, himc, + debugstr_w(srcW), listW, lengthW, flags ); + + if (!ime->hIME || !ime->pImeConversionList) return 0; + + if (is_kbd_ime_unicode( ime )) + ret = ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); + else { - if (is_kbd_ime_unicode(immHkl)) - return immHkl->pImeConversionList(hIMC,pSrc,lpDst,dwBufLen,uFlag); + CANDIDATELIST *listA; + char *srcA = strdupWtoA( srcW ); + DWORD lengthA = ime->pImeConversionList( himc, (const WCHAR *)srcA, NULL, 0, flags ); + + if (!(listA = HeapAlloc( GetProcessHeap(), 0, lengthA ))) ret = 0; else { - LPCANDIDATELIST lpaDst; - DWORD ret = 0, len; - LPSTR paSrc = strdupWtoA(pSrc); - - len = immHkl->pImeConversionList(hIMC, (LPCWSTR)paSrc, NULL, 0, uFlag); - lpaDst = HeapAlloc(GetProcessHeap(), 0, len); - if ( lpaDst ) - { - immHkl->pImeConversionList(hIMC, (LPCWSTR)paSrc, lpaDst, len, uFlag); - ret = convert_candidatelist_AtoW( lpaDst, lpDst, dwBufLen); - HeapFree(GetProcessHeap(), 0, lpaDst); - } - HeapFree(GetProcessHeap(), 0, paSrc); - - return ret; + ime->pImeConversionList( himc, (const WCHAR *)srcA, listA, lengthA, flags ); + ret = convert_candidatelist_AtoW( listA, listW, lengthW ); + HeapFree( GetProcessHeap(), 0, listA ); } + HeapFree( GetProcessHeap(), 0, srcA ); } - else - return 0; + + return ret; } /*********************************************************************** From 3df25416686a36bd58f3e36fde41d1abce50609b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 094/758] imm32: Reorder control flow in ImmGetProperty. (cherry picked from commit 03f1780653daf35b8654fa9610d153c47b319a8c) --- dlls/imm32/imm.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 6a5ee4f2217..de455c71b9d 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1856,29 +1856,29 @@ BOOL WINAPI ImmGetOpenStatus(HIMC hIMC) /*********************************************************************** * ImmGetProperty (IMM32.@) */ -DWORD WINAPI ImmGetProperty(HKL hKL, DWORD fdwIndex) +DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) { - DWORD rc = 0; - struct ime *kbd; + struct ime *ime; + DWORD ret; + + TRACE( "hkl %p, index %lu.\n", hkl, index ); - TRACE("(%p, %ld)\n", hKL, fdwIndex); - kbd = IMM_GetImmHkl(hKL); + ime = IMM_GetImmHkl( hkl ); + if (!ime || !ime->hIME) return 0; - if (kbd && kbd->hIME) + switch (index) { - switch (fdwIndex) - { - case IGP_PROPERTY: rc = kbd->imeInfo.fdwProperty; break; - case IGP_CONVERSION: rc = kbd->imeInfo.fdwConversionCaps; break; - case IGP_SENTENCE: rc = kbd->imeInfo.fdwSentenceCaps; break; - case IGP_SETCOMPSTR: rc = kbd->imeInfo.fdwSCSCaps; break; - case IGP_SELECT: rc = kbd->imeInfo.fdwSelectCaps; break; - case IGP_GETIMEVERSION: rc = IMEVER_0400; break; - case IGP_UI: rc = 0; break; - default: rc = 0; - } + case IGP_PROPERTY: ret = ime->imeInfo.fdwProperty; break; + case IGP_CONVERSION: ret = ime->imeInfo.fdwConversionCaps; break; + case IGP_SENTENCE: ret = ime->imeInfo.fdwSentenceCaps; break; + case IGP_SETCOMPSTR: ret = ime->imeInfo.fdwSCSCaps; break; + case IGP_SELECT: ret = ime->imeInfo.fdwSelectCaps; break; + case IGP_GETIMEVERSION: ret = IMEVER_0400; break; + case IGP_UI: ret = 0; break; + default: ret = 0; break; } - return rc; + + return ret; } /*********************************************************************** From f4b8b41b25e36f70c9fc359595627b4f0381cc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 095/758] imm32: Reorder control flow in ImmGetRegisterWordStyleA. (cherry picked from commit 4693428b0562030c2454b6601bfb29d001bfad1b) --- dlls/imm32/imm.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index de455c71b9d..2c9c04fc684 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1884,29 +1884,26 @@ DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) /*********************************************************************** * ImmGetRegisterWordStyleA (IMM32.@) */ -UINT WINAPI ImmGetRegisterWordStyleA( - HKL hKL, UINT nItem, LPSTYLEBUFA lpStyleBuf) +UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %d, %p):\n", hKL, nItem, lpStyleBuf); - if (immHkl->hIME && immHkl->pImeGetRegisterWordStyle) - { - if (!is_kbd_ime_unicode(immHkl)) - return immHkl->pImeGetRegisterWordStyle(nItem,(LPSTYLEBUFW)lpStyleBuf); - else - { - STYLEBUFW sbw; - UINT rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + UINT ret; - rc = immHkl->pImeGetRegisterWordStyle(nItem,&sbw); - WideCharToMultiByte(CP_ACP, 0, sbw.szDescription, -1, - lpStyleBuf->szDescription, 32, NULL, NULL); - lpStyleBuf->dwStyle = sbw.dwStyle; - return rc; - } - } + TRACE( "hkl %p, count %u, styleA %p.\n", hkl, count, styleA ); + + if (!ime->hIME || !ime->pImeGetRegisterWordStyle) return 0; + + if (!is_kbd_ime_unicode( ime )) + ret = ime->pImeGetRegisterWordStyle( count, (STYLEBUFW *)styleA ); else - return 0; + { + STYLEBUFW styleW; + ret = ime->pImeGetRegisterWordStyle( count, &styleW ); + WideCharToMultiByte( CP_ACP, 0, styleW.szDescription, -1, styleA->szDescription, 32, NULL, NULL ); + styleA->dwStyle = styleW.dwStyle; + } + + return ret; } /*********************************************************************** From e20c8f82a2f4752e89ea8803fc24146a97202ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 096/758] imm32: Reorder control flow in ImmGetRegisterWordStyleW. (cherry picked from commit 1bc81cd8aedcd76072710c1403467b9b79e45622) --- dlls/imm32/imm.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 2c9c04fc684..ea5dd127a9e 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1909,29 +1909,26 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) /*********************************************************************** * ImmGetRegisterWordStyleW (IMM32.@) */ -UINT WINAPI ImmGetRegisterWordStyleW( - HKL hKL, UINT nItem, LPSTYLEBUFW lpStyleBuf) +UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %d, %p):\n", hKL, nItem, lpStyleBuf); - if (immHkl->hIME && immHkl->pImeGetRegisterWordStyle) - { - if (is_kbd_ime_unicode(immHkl)) - return immHkl->pImeGetRegisterWordStyle(nItem,lpStyleBuf); - else - { - STYLEBUFA sba; - UINT rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + UINT ret; - rc = immHkl->pImeGetRegisterWordStyle(nItem,(LPSTYLEBUFW)&sba); - MultiByteToWideChar(CP_ACP, 0, sba.szDescription, -1, - lpStyleBuf->szDescription, 32); - lpStyleBuf->dwStyle = sba.dwStyle; - return rc; - } - } + TRACE( "hkl %p, count %u, styleW %p.\n", hkl, count, styleW ); + + if (!ime->hIME || !ime->pImeGetRegisterWordStyle) return 0; + + if (is_kbd_ime_unicode( ime )) + ret = ime->pImeGetRegisterWordStyle( count, styleW ); else - return 0; + { + STYLEBUFA styleA; + ret = ime->pImeGetRegisterWordStyle( count, (STYLEBUFW *)&styleA ); + MultiByteToWideChar( CP_ACP, 0, styleA.szDescription, -1, styleW->szDescription, 32 ); + styleW->dwStyle = styleA.dwStyle; + } + + return ret; } /*********************************************************************** From 7148e09f05b56c532a035dbdb8bf9df32a9549ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 097/758] imm32: Reorder control flow in ImmRegisterWordA. (cherry picked from commit cda7a1338e1329299ba9347d58fade15530fbb4b) --- dlls/imm32/imm.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index ea5dd127a9e..c05a4845f5d 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2138,31 +2138,26 @@ BOOL WINAPI ImmNotifyIME( /*********************************************************************** * ImmRegisterWordA (IMM32.@) */ -BOOL WINAPI ImmRegisterWordA( - HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszRegister) +BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const char *stringA ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_a(lpszReading), dwStyle, - debugstr_a(lpszRegister)); - if (immHkl->hIME && immHkl->pImeRegisterWord) - { - if (!is_kbd_ime_unicode(immHkl)) - return immHkl->pImeRegisterWord((LPCWSTR)lpszReading,dwStyle, - (LPCWSTR)lpszRegister); - else - { - LPWSTR lpszwReading = strdupAtoW(lpszReading); - LPWSTR lpszwRegister = strdupAtoW(lpszRegister); - BOOL rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + BOOL ret; - rc = immHkl->pImeRegisterWord(lpszwReading,dwStyle,lpszwRegister); - HeapFree(GetProcessHeap(),0,lpszwReading); - HeapFree(GetProcessHeap(),0,lpszwRegister); - return rc; - } - } + TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); + + if (!ime->hIME || !ime->pImeRegisterWord) return FALSE; + + if (!is_kbd_ime_unicode( ime )) + ret = ime->pImeRegisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); else - return FALSE; + { + WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); + ret = ime->pImeRegisterWord( readingW, style, stringW ); + HeapFree( GetProcessHeap(), 0, readingW ); + HeapFree( GetProcessHeap(), 0, stringW ); + } + + return ret; } /*********************************************************************** From e925cb14ad508dff7e2daaa1279ed36d49298c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 098/758] imm32: Reorder control flow in ImmRegisterWordW. (cherry picked from commit 034148e6a146871fb1e3f85eac8d560b29e6aaec) --- dlls/imm32/imm.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index c05a4845f5d..a881ef76224 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2163,31 +2163,26 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const /*********************************************************************** * ImmRegisterWordW (IMM32.@) */ -BOOL WINAPI ImmRegisterWordW( - HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister) +BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const WCHAR *stringW ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_w(lpszReading), dwStyle, - debugstr_w(lpszRegister)); - if (immHkl->hIME && immHkl->pImeRegisterWord) - { - if (is_kbd_ime_unicode(immHkl)) - return immHkl->pImeRegisterWord(lpszReading,dwStyle,lpszRegister); - else - { - LPSTR lpszaReading = strdupWtoA(lpszReading); - LPSTR lpszaRegister = strdupWtoA(lpszRegister); - BOOL rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + BOOL ret; - rc = immHkl->pImeRegisterWord((LPCWSTR)lpszaReading,dwStyle, - (LPCWSTR)lpszaRegister); - HeapFree(GetProcessHeap(),0,lpszaReading); - HeapFree(GetProcessHeap(),0,lpszaRegister); - return rc; - } - } + TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); + + if (!ime->hIME || !ime->pImeRegisterWord) return FALSE; + + if (is_kbd_ime_unicode( ime )) + ret = ime->pImeRegisterWord( readingW, style, stringW ); else - return FALSE; + { + char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); + ret = ime->pImeRegisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); + HeapFree( GetProcessHeap(), 0, readingA ); + HeapFree( GetProcessHeap(), 0, stringA ); + } + + return ret; } /*********************************************************************** From 6d8226be6f04c2675e56bd2ac49d9ecdf6631527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 099/758] imm32: Reorder control flow in ImmUnregisterWordA. (cherry picked from commit 4778d1f2bc54ec05895ef7ce895b062abe132ce6) --- dlls/imm32/imm.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index a881ef76224..f0dfd755229 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2618,31 +2618,26 @@ BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID) /*********************************************************************** * ImmUnregisterWordA (IMM32.@) */ -BOOL WINAPI ImmUnregisterWordA( - HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszUnregister) +BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, const char *stringA ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_a(lpszReading), dwStyle, - debugstr_a(lpszUnregister)); - if (immHkl->hIME && immHkl->pImeUnregisterWord) - { - if (!is_kbd_ime_unicode(immHkl)) - return immHkl->pImeUnregisterWord((LPCWSTR)lpszReading,dwStyle, - (LPCWSTR)lpszUnregister); - else - { - LPWSTR lpszwReading = strdupAtoW(lpszReading); - LPWSTR lpszwUnregister = strdupAtoW(lpszUnregister); - BOOL rc; + struct ime *ime = IMM_GetImmHkl( hkl ); + BOOL ret; - rc = immHkl->pImeUnregisterWord(lpszwReading,dwStyle,lpszwUnregister); - HeapFree(GetProcessHeap(),0,lpszwReading); - HeapFree(GetProcessHeap(),0,lpszwUnregister); - return rc; - } - } + TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); + + if (!ime->hIME || !ime->pImeUnregisterWord) return FALSE; + + if (!is_kbd_ime_unicode( ime )) + ret = ime->pImeUnregisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); else - return FALSE; + { + WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); + ret = ime->pImeUnregisterWord( readingW, style, stringW ); + HeapFree( GetProcessHeap(), 0, readingW ); + HeapFree( GetProcessHeap(), 0, stringW ); + } + + return ret; } /*********************************************************************** From 08230403faba691084d9a2ab7de992657c65e244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 100/758] imm32: Reorder control flow in ImmUnregisterWordW. (cherry picked from commit ad2ca1e99ca34804e5c6297cb31c6c0f08d22e61) --- dlls/imm32/imm.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index f0dfd755229..2fb0e06ac77 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2643,31 +2643,26 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons /*********************************************************************** * ImmUnregisterWordW (IMM32.@) */ -BOOL WINAPI ImmUnregisterWordW( - HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister) +BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const WCHAR *stringW ) { - struct ime *immHkl = IMM_GetImmHkl( hKL ); - TRACE("(%p, %s, %ld, %s):\n", hKL, debugstr_w(lpszReading), dwStyle, - debugstr_w(lpszUnregister)); - if (immHkl->hIME && immHkl->pImeUnregisterWord) + struct ime *ime = IMM_GetImmHkl( hkl ); + BOOL ret; + + TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); + + if (!ime->hIME || !ime->pImeUnregisterWord) return FALSE; + + if (is_kbd_ime_unicode( ime )) + ret = ime->pImeUnregisterWord( readingW, style, stringW ); + else { - if (is_kbd_ime_unicode(immHkl)) - return immHkl->pImeUnregisterWord(lpszReading,dwStyle,lpszUnregister); - else - { - LPSTR lpszaReading = strdupWtoA(lpszReading); - LPSTR lpszaUnregister = strdupWtoA(lpszUnregister); - BOOL rc; - - rc = immHkl->pImeUnregisterWord((LPCWSTR)lpszaReading,dwStyle, - (LPCWSTR)lpszaUnregister); - HeapFree(GetProcessHeap(),0,lpszaReading); - HeapFree(GetProcessHeap(),0,lpszaUnregister); - return rc; - } + char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); + ret = ime->pImeUnregisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); + HeapFree( GetProcessHeap(), 0, readingA ); + HeapFree( GetProcessHeap(), 0, stringA ); } - else - return FALSE; + + return ret; } /*********************************************************************** From 5deae51b45a4a4a35dc0787846db5e9d2ff27e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 101/758] imm32: Reorder control flow in ImmGetImeMenuItemsA. (cherry picked from commit d07cec302136d32ad3adc92910f15a069dcdd97e) --- dlls/imm32/imm.c | 90 +++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 2fb0e06ac77..c7fdb096fe2 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2668,72 +2668,62 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con /*********************************************************************** * ImmGetImeMenuItemsA (IMM32.@) */ -DWORD WINAPI ImmGetImeMenuItemsA( HIMC hIMC, DWORD dwFlags, DWORD dwType, - LPIMEMENUITEMINFOA lpImeParentMenu, LPIMEMENUITEMINFOA lpImeMenu, - DWORD dwSize) +DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOA *parentA, + IMEMENUITEMINFOA *menuA, DWORD size ) { - InputContextData *data = get_imc_data(hIMC); - TRACE("(%p, %li, %li, %p, %p, %li):\n", hIMC, dwFlags, dwType, - lpImeParentMenu, lpImeMenu, dwSize); + InputContextData *data = get_imc_data( himc ); + DWORD ret; + + TRACE( "himc %p, flags %#lx, type %lu, parentA %p, menuA %p, size %lu.\n", + himc, flags, type, parentA, menuA, size ); if (!data) { - SetLastError(ERROR_INVALID_HANDLE); + SetLastError( ERROR_INVALID_HANDLE ); return 0; } - if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems) + if (!data->immKbd->hIME || !data->immKbd->pImeGetImeMenuItems) return 0; + + if (!is_himc_ime_unicode( data ) || (!parentA && !menuA)) + ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, (IMEMENUITEMINFOW *)parentA, + (IMEMENUITEMINFOW *)menuA, size ); + else { - if (!is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu)) - return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType, - (IMEMENUITEMINFOW*)lpImeParentMenu, - (IMEMENUITEMINFOW*)lpImeMenu, dwSize); + IMEMENUITEMINFOW tmpW, *menuW, *parentW = parentA ? &tmpW : NULL; + + if (!menuA) menuW = NULL; else { - IMEMENUITEMINFOW lpImeParentMenuW; - IMEMENUITEMINFOW *lpImeMenuW, *parent = NULL; - DWORD rc; - - if (lpImeParentMenu) - parent = &lpImeParentMenuW; - if (lpImeMenu) - { - int count = dwSize / sizeof(LPIMEMENUITEMINFOA); - dwSize = count * sizeof(IMEMENUITEMINFOW); - lpImeMenuW = HeapAlloc(GetProcessHeap(), 0, dwSize); - } - else - lpImeMenuW = NULL; + int count = size / sizeof(LPIMEMENUITEMINFOA); + size = count * sizeof(IMEMENUITEMINFOW); + menuW = HeapAlloc( GetProcessHeap(), 0, size ); + } - rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType, - parent, lpImeMenuW, dwSize); + ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); - if (lpImeParentMenu) - { - memcpy(lpImeParentMenu,&lpImeParentMenuW,sizeof(IMEMENUITEMINFOA)); - lpImeParentMenu->hbmpItem = lpImeParentMenuW.hbmpItem; - WideCharToMultiByte(CP_ACP, 0, lpImeParentMenuW.szString, - -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE, - NULL, NULL); - } - if (lpImeMenu && rc) + if (parentA) + { + memcpy( parentA, parentW, sizeof(IMEMENUITEMINFOA) ); + parentA->hbmpItem = parentW->hbmpItem; + WideCharToMultiByte( CP_ACP, 0, parentW->szString, -1, parentA->szString, + IMEMENUITEM_STRING_SIZE, NULL, NULL ); + } + if (menuA && ret) + { + unsigned int i; + for (i = 0; i < ret; i++) { - unsigned int i; - for (i = 0; i < rc; i++) - { - memcpy(&lpImeMenu[i],&lpImeMenuW[1],sizeof(IMEMENUITEMINFOA)); - lpImeMenu[i].hbmpItem = lpImeMenuW[i].hbmpItem; - WideCharToMultiByte(CP_ACP, 0, lpImeMenuW[i].szString, - -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE, - NULL, NULL); - } + memcpy( &menuA[i], &menuW[1], sizeof(IMEMENUITEMINFOA) ); + menuA[i].hbmpItem = menuW[i].hbmpItem; + WideCharToMultiByte( CP_ACP, 0, menuW[i].szString, -1, menuA[i].szString, + IMEMENUITEM_STRING_SIZE, NULL, NULL ); } - HeapFree(GetProcessHeap(),0,lpImeMenuW); - return rc; } + HeapFree( GetProcessHeap(), 0, menuW ); } - else - return 0; + + return ret; } /*********************************************************************** From dc530e8c370e3f9cf03d09d6e2ae6928c4202d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 13:30:36 +0100 Subject: [PATCH 102/758] imm32: Reorder control flow in ImmGetImeMenuItemsW. (cherry picked from commit 87e0c10b4c867f94570f142c6a30d2d5145973dc) --- dlls/imm32/imm.c | 86 +++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index c7fdb096fe2..634c4ff472c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2729,70 +2729,60 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE /*********************************************************************** * ImmGetImeMenuItemsW (IMM32.@) */ -DWORD WINAPI ImmGetImeMenuItemsW( HIMC hIMC, DWORD dwFlags, DWORD dwType, - LPIMEMENUITEMINFOW lpImeParentMenu, LPIMEMENUITEMINFOW lpImeMenu, - DWORD dwSize) +DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parentW, + IMEMENUITEMINFOW *menuW, DWORD size ) { - InputContextData *data = get_imc_data(hIMC); - TRACE("(%p, %li, %li, %p, %p, %li):\n", hIMC, dwFlags, dwType, - lpImeParentMenu, lpImeMenu, dwSize); + InputContextData *data = get_imc_data( himc ); + DWORD ret; + + TRACE( "himc %p, flags %#lx, type %lu, parentW %p, menuW %p, size %lu.\n", + himc, flags, type, parentW, menuW, size ); if (!data) { - SetLastError(ERROR_INVALID_HANDLE); + SetLastError( ERROR_INVALID_HANDLE ); return 0; } - if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems) + if (!data->immKbd->hIME || !data->immKbd->pImeGetImeMenuItems) return 0; + + if (is_himc_ime_unicode( data ) || (!parentW && !menuW)) + ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); + else { - if (is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu)) - return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType, - lpImeParentMenu, lpImeMenu, dwSize); + IMEMENUITEMINFOA tmpA, *menuA, *parentA = parentW ? &tmpA : NULL; + + if (!menuW) menuA = NULL; else { - IMEMENUITEMINFOA lpImeParentMenuA; - IMEMENUITEMINFOA *lpImeMenuA, *parent = NULL; - DWORD rc; - - if (lpImeParentMenu) - parent = &lpImeParentMenuA; - if (lpImeMenu) - { - int count = dwSize / sizeof(LPIMEMENUITEMINFOW); - dwSize = count * sizeof(IMEMENUITEMINFOA); - lpImeMenuA = HeapAlloc(GetProcessHeap(), 0, dwSize); - } - else - lpImeMenuA = NULL; + int count = size / sizeof(LPIMEMENUITEMINFOW); + size = count * sizeof(IMEMENUITEMINFOA); + menuA = HeapAlloc( GetProcessHeap(), 0, size ); + } - rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType, - (IMEMENUITEMINFOW*)parent, - (IMEMENUITEMINFOW*)lpImeMenuA, dwSize); + ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, (IMEMENUITEMINFOW *)parentA, + (IMEMENUITEMINFOW *)menuA, size ); - if (lpImeParentMenu) - { - memcpy(lpImeParentMenu,&lpImeParentMenuA,sizeof(IMEMENUITEMINFOA)); - lpImeParentMenu->hbmpItem = lpImeParentMenuA.hbmpItem; - MultiByteToWideChar(CP_ACP, 0, lpImeParentMenuA.szString, - -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE); - } - if (lpImeMenu && rc) + if (parentW) + { + memcpy( parentW, parentA, sizeof(IMEMENUITEMINFOA) ); + parentW->hbmpItem = parentA->hbmpItem; + MultiByteToWideChar( CP_ACP, 0, parentA->szString, -1, parentW->szString, IMEMENUITEM_STRING_SIZE ); + } + if (menuW && ret) + { + unsigned int i; + for (i = 0; i < ret; i++) { - unsigned int i; - for (i = 0; i < rc; i++) - { - memcpy(&lpImeMenu[i],&lpImeMenuA[1],sizeof(IMEMENUITEMINFOA)); - lpImeMenu[i].hbmpItem = lpImeMenuA[i].hbmpItem; - MultiByteToWideChar(CP_ACP, 0, lpImeMenuA[i].szString, - -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE); - } + memcpy( &menuW[i], &menuA[1], sizeof(IMEMENUITEMINFOA) ); + menuW[i].hbmpItem = menuA[i].hbmpItem; + MultiByteToWideChar( CP_ACP, 0, menuA[i].szString, -1, menuW[i].szString, IMEMENUITEM_STRING_SIZE ); } - HeapFree(GetProcessHeap(),0,lpImeMenuA); - return rc; } + HeapFree( GetProcessHeap(), 0, menuA ); } - else - return 0; + + return ret; } /*********************************************************************** From 7732904ac939a7edb5fa7512aa3357413ab6258a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 17:25:39 +0100 Subject: [PATCH 103/758] imm32: Avoid casts when calling into A/W IME. (cherry picked from commit 1ac7125813ca37e88da618695bd78d0bf88d7bb4) --- dlls/imm32/imm.c | 50 ++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 634c4ff472c..a31e89d00c2 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -63,7 +63,7 @@ struct ime HWND UIWnd; /* Function Pointers */ - BOOL (WINAPI *pImeInquire)(IMEINFO *, WCHAR *, DWORD); + BOOL (WINAPI *pImeInquire)(IMEINFO *, void *, DWORD); BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *); BOOL (WINAPI *pImeDestroy)(UINT); LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *); @@ -71,14 +71,14 @@ struct ime BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL); UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, TRANSMSGLIST *, UINT, HIMC); BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD); - BOOL (WINAPI *pImeRegisterWord)(const WCHAR *, DWORD, const WCHAR *); - BOOL (WINAPI *pImeUnregisterWord)(const WCHAR *, DWORD, const WCHAR *); - UINT (WINAPI *pImeEnumRegisterWord)(REGISTERWORDENUMPROCW, const WCHAR *, DWORD, const WCHAR *, void *); - BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void *, DWORD, const void *, DWORD); - DWORD (WINAPI *pImeConversionList)(HIMC, const WCHAR *, CANDIDATELIST *, DWORD, UINT); - BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *); - UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *); - DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD); + BOOL (WINAPI *pImeRegisterWord)(const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*); + BOOL (WINAPI *pImeUnregisterWord)(const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*); + UINT (WINAPI *pImeEnumRegisterWord)(void */*REGISTERWORDENUMPROCW*/, const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*, void *); + BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*, DWORD); + DWORD (WINAPI *pImeConversionList)(HIMC, const void/*TCHAR*/*, CANDIDATELIST*, DWORD, UINT); + UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, void/*STYLEBUFW*/*); + BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE*); + DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, void/*IMEMENUITEMINFOW*/*, void/*IMEMENUITEMINFOW*/*, DWORD); }; static HRESULT (WINAPI *pCoRevokeInitializeSpy)(ULARGE_INTEGER cookie); @@ -934,12 +934,11 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch if (!ime->hIME || !ime->pImeEnumRegisterWord) return 0; if (!is_kbd_ime_unicode( ime )) - ret = ime->pImeEnumRegisterWord( (REGISTERWORDENUMPROCW)procA, (const WCHAR *)readingA, - style, (const WCHAR *)stringA, user ); + ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user ); else { WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); - ret = ime->pImeEnumRegisterWord( (REGISTERWORDENUMPROCW)procA, readingW, style, stringW, user ); + ret = ime->pImeEnumRegisterWord( procA, readingW, style, stringW, user ); HeapFree( GetProcessHeap(), 0, readingW ); HeapFree( GetProcessHeap(), 0, stringW ); } @@ -966,8 +965,7 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC else { char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); - ret = ime->pImeEnumRegisterWord( procW, (const WCHAR *)readingA, style, - (const WCHAR *)stringA, user ); + ret = ime->pImeEnumRegisterWord( procW, readingA, style, stringA, user ); HeapFree( GetProcessHeap(), 0, readingA ); HeapFree( GetProcessHeap(), 0, stringA ); } @@ -1597,7 +1595,7 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID if (!ime->hIME || !ime->pImeConversionList) return 0; if (!is_kbd_ime_unicode( ime )) - ret = ime->pImeConversionList( himc, (const WCHAR *)srcA, listA, lengthA, flags ); + ret = ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); else { CANDIDATELIST *listW; @@ -1637,12 +1635,12 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI { CANDIDATELIST *listA; char *srcA = strdupWtoA( srcW ); - DWORD lengthA = ime->pImeConversionList( himc, (const WCHAR *)srcA, NULL, 0, flags ); + DWORD lengthA = ime->pImeConversionList( himc, srcA, NULL, 0, flags ); if (!(listA = HeapAlloc( GetProcessHeap(), 0, lengthA ))) ret = 0; else { - ime->pImeConversionList( himc, (const WCHAR *)srcA, listA, lengthA, flags ); + ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); ret = convert_candidatelist_AtoW( listA, listW, lengthW ); HeapFree( GetProcessHeap(), 0, listA ); } @@ -1894,7 +1892,7 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) if (!ime->hIME || !ime->pImeGetRegisterWordStyle) return 0; if (!is_kbd_ime_unicode( ime )) - ret = ime->pImeGetRegisterWordStyle( count, (STYLEBUFW *)styleA ); + ret = ime->pImeGetRegisterWordStyle( count, styleA ); else { STYLEBUFW styleW; @@ -1923,7 +1921,7 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) else { STYLEBUFA styleA; - ret = ime->pImeGetRegisterWordStyle( count, (STYLEBUFW *)&styleA ); + ret = ime->pImeGetRegisterWordStyle( count, &styleA ); MultiByteToWideChar( CP_ACP, 0, styleA.szDescription, -1, styleW->szDescription, 32 ); styleW->dwStyle = styleA.dwStyle; } @@ -2148,7 +2146,7 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const if (!ime->hIME || !ime->pImeRegisterWord) return FALSE; if (!is_kbd_ime_unicode( ime )) - ret = ime->pImeRegisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); + ret = ime->pImeRegisterWord( readingA, style, stringA ); else { WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); @@ -2177,7 +2175,7 @@ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const else { char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); - ret = ime->pImeRegisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); + ret = ime->pImeRegisterWord( readingA, style, stringA ); HeapFree( GetProcessHeap(), 0, readingA ); HeapFree( GetProcessHeap(), 0, stringA ); } @@ -2628,7 +2626,7 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons if (!ime->hIME || !ime->pImeUnregisterWord) return FALSE; if (!is_kbd_ime_unicode( ime )) - ret = ime->pImeUnregisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); + ret = ime->pImeUnregisterWord( readingA, style, stringA ); else { WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); @@ -2657,7 +2655,7 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con else { char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); - ret = ime->pImeUnregisterWord( (const WCHAR *)readingA, style, (const WCHAR *)stringA ); + ret = ime->pImeUnregisterWord( readingA, style, stringA ); HeapFree( GetProcessHeap(), 0, readingA ); HeapFree( GetProcessHeap(), 0, stringA ); } @@ -2686,8 +2684,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE if (!data->immKbd->hIME || !data->immKbd->pImeGetImeMenuItems) return 0; if (!is_himc_ime_unicode( data ) || (!parentA && !menuA)) - ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, (IMEMENUITEMINFOW *)parentA, - (IMEMENUITEMINFOW *)menuA, size ); + ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); else { IMEMENUITEMINFOW tmpW, *menuW, *parentW = parentA ? &tmpW : NULL; @@ -2760,8 +2757,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE menuA = HeapAlloc( GetProcessHeap(), 0, size ); } - ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, (IMEMENUITEMINFOW *)parentA, - (IMEMENUITEMINFOW *)menuA, size ); + ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); if (parentW) { From afe15763ff06b70bdf49bd674a7961b41ff6e00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 14:10:59 +0100 Subject: [PATCH 104/758] imm32: Fail to load IME on any missing entry. Testing shows that this is what native does. (cherry picked from commit 84cec42e62c24a87298b209b53dcf1ff8a6ad626) --- dlls/imm32/imm.c | 124 +++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index a31e89d00c2..e2c2f7bfccf 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -492,7 +492,6 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) } /* struct ime loading and freeing */ -#define LOAD_FUNCPTR(f) if((ptr->p##f = (LPVOID)GetProcAddress(ptr->hIME, #f)) == NULL){WARN("Can't find function %s in ime\n", #f);} static struct ime *IMM_GetImmHkl( HKL hkl ) { struct ime *ptr; @@ -512,54 +511,52 @@ static struct ime *IMM_GetImmHkl( HKL hkl ) ptr->hkl = hkl; if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename); if (!ptr->hIME) ptr->hIME = load_graphics_driver(); - if (ptr->hIME) + if (!ptr->hIME) goto failed; + +#define LOAD_FUNCPTR( f ) \ + if (!(ptr->p##f = (void *)GetProcAddress( ptr->hIME, #f ))) \ + { \ + WARN( "Can't find function %s in ime\n", #f ); \ + goto failed; \ + } + LOAD_FUNCPTR( ImeInquire ); + LOAD_FUNCPTR( ImeDestroy ); + LOAD_FUNCPTR( ImeSelect ); + LOAD_FUNCPTR( ImeConfigure ); + LOAD_FUNCPTR( ImeEscape ); + LOAD_FUNCPTR( ImeSetActiveContext ); + LOAD_FUNCPTR( ImeToAsciiEx ); + LOAD_FUNCPTR( NotifyIME ); + LOAD_FUNCPTR( ImeRegisterWord ); + LOAD_FUNCPTR( ImeUnregisterWord ); + LOAD_FUNCPTR( ImeEnumRegisterWord ); + LOAD_FUNCPTR( ImeSetCompositionString ); + LOAD_FUNCPTR( ImeConversionList ); + LOAD_FUNCPTR( ImeProcessKey ); + LOAD_FUNCPTR( ImeGetRegisterWordStyle ); + LOAD_FUNCPTR( ImeGetImeMenuItems ); +#undef LOAD_FUNCPTR + + if (!ptr->pImeInquire( &ptr->imeInfo, ptr->imeClassName, 0 )) goto failed; + + /* make sure our classname is WCHAR */ + if (!is_kbd_ime_unicode( ptr )) { - LOAD_FUNCPTR(ImeInquire); - if (!ptr->pImeInquire || !ptr->pImeInquire(&ptr->imeInfo, ptr->imeClassName, 0)) - { - FreeLibrary(ptr->hIME); - ptr->hIME = NULL; - } - else - { - LOAD_FUNCPTR(ImeDestroy); - LOAD_FUNCPTR(ImeSelect); - if (!ptr->pImeSelect || !ptr->pImeDestroy) - { - FreeLibrary(ptr->hIME); - ptr->hIME = NULL; - } - else - { - LOAD_FUNCPTR(ImeConfigure); - LOAD_FUNCPTR(ImeEscape); - LOAD_FUNCPTR(ImeSetActiveContext); - LOAD_FUNCPTR(ImeToAsciiEx); - LOAD_FUNCPTR(NotifyIME); - LOAD_FUNCPTR(ImeRegisterWord); - LOAD_FUNCPTR(ImeUnregisterWord); - LOAD_FUNCPTR(ImeEnumRegisterWord); - LOAD_FUNCPTR(ImeSetCompositionString); - LOAD_FUNCPTR(ImeConversionList); - LOAD_FUNCPTR(ImeProcessKey); - LOAD_FUNCPTR(ImeGetRegisterWordStyle); - LOAD_FUNCPTR(ImeGetImeMenuItems); - /* make sure our classname is WCHAR */ - if (!is_kbd_ime_unicode(ptr)) - { - WCHAR bufW[17]; - MultiByteToWideChar(CP_ACP, 0, (LPSTR)ptr->imeClassName, - -1, bufW, 17); - lstrcpyW(ptr->imeClassName, bufW); - } - } - } + WCHAR bufW[17]; + MultiByteToWideChar( CP_ACP, 0, (LPSTR)ptr->imeClassName, -1, bufW, 17 ); + lstrcpyW( ptr->imeClassName, bufW ); } + list_add_head(&ImmHklList,&ptr->entry); + return ptr; + +failed: + if (ptr->hIME) FreeLibrary( ptr->hIME ); + ptr->hIME = NULL; + list_add_head( &ImmHklList, &ptr->entry ); return ptr; } -#undef LOAD_FUNCPTR static void IMM_FreeAllImmHkl(void) @@ -684,8 +681,7 @@ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) { data->IMC.hWnd = activate ? hwnd : NULL; - if (data->immKbd->hIME && data->immKbd->pImeSetActiveContext) - data->immKbd->pImeSetActiveContext(himc, activate); + if (data->immKbd->hIME) data->immKbd->pImeSetActiveContext(himc, activate); } if (IsWindow(hwnd)) @@ -768,7 +764,7 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; - if (!ime->hIME || !ime->pImeConfigure) return FALSE; + if (!ime->hIME) return FALSE; if (mode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); @@ -797,7 +793,7 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; - if (!ime->hIME || !ime->pImeConfigure) return FALSE; + if (!ime->hIME) return FALSE; if (mode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); @@ -931,7 +927,7 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch TRACE( "hkl %p, procA %p, readingA %s, style %lu, stringA %s, user %p.\n", hkl, procA, debugstr_a(readingA), style, debugstr_a(stringA), user ); - if (!ime->hIME || !ime->pImeEnumRegisterWord) return 0; + if (!ime->hIME) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user ); @@ -958,7 +954,7 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC TRACE( "hkl %p, procW %p, readingW %s, style %lu, stringW %s, user %p.\n", hkl, procW, debugstr_w(readingW), style, debugstr_w(stringW), user ); - if (!ime->hIME || !ime->pImeEnumRegisterWord) return 0; + if (!ime->hIME) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user ); @@ -993,7 +989,7 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); - if (!ime->hIME || !ime->pImeEscape) return 0; + if (!ime->hIME) return 0; if (!EscapeRequiresWA( code ) || !is_kbd_ime_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); @@ -1025,7 +1021,7 @@ LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); - if (!ime->hIME || !ime->pImeEscape) return 0; + if (!ime->hIME) return 0; if (!EscapeRequiresWA( code ) || is_kbd_ime_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); @@ -1592,7 +1588,7 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID TRACE( "hkl %p, himc %p, srcA %s, listA %p, lengthA %lu, flags %#x.\n", hkl, himc, debugstr_a(srcA), listA, lengthA, flags ); - if (!ime->hIME || !ime->pImeConversionList) return 0; + if (!ime->hIME) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); @@ -1627,7 +1623,7 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI TRACE( "hkl %p, himc %p, srcW %s, listW %p, lengthW %lu, flags %#x.\n", hkl, himc, debugstr_w(srcW), listW, lengthW, flags ); - if (!ime->hIME || !ime->pImeConversionList) return 0; + if (!ime->hIME) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); @@ -1889,7 +1885,7 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) TRACE( "hkl %p, count %u, styleA %p.\n", hkl, count, styleA ); - if (!ime->hIME || !ime->pImeGetRegisterWordStyle) return 0; + if (!ime->hIME) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleA ); @@ -1914,7 +1910,7 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) TRACE( "hkl %p, count %u, styleW %p.\n", hkl, count, styleW ); - if (!ime->hIME || !ime->pImeGetRegisterWordStyle) return 0; + if (!ime->hIME) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleW ); @@ -2125,7 +2121,7 @@ BOOL WINAPI ImmNotifyIME( return FALSE; } - if (!data || ! data->immKbd->pNotifyIME) + if (!data) { return FALSE; } @@ -2143,7 +2139,7 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); - if (!ime->hIME || !ime->pImeRegisterWord) return FALSE; + if (!ime->hIME) return FALSE; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeRegisterWord( readingA, style, stringA ); @@ -2168,7 +2164,7 @@ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); - if (!ime->hIME || !ime->pImeRegisterWord) return FALSE; + if (!ime->hIME) return FALSE; if (is_kbd_ime_unicode( ime )) ret = ime->pImeRegisterWord( readingW, style, stringW ); @@ -2623,7 +2619,7 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); - if (!ime->hIME || !ime->pImeUnregisterWord) return FALSE; + if (!ime->hIME) return FALSE; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeUnregisterWord( readingA, style, stringA ); @@ -2648,7 +2644,7 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); - if (!ime->hIME || !ime->pImeUnregisterWord) return FALSE; + if (!ime->hIME) return FALSE; if (is_kbd_ime_unicode( ime )) ret = ime->pImeUnregisterWord( readingW, style, stringW ); @@ -2681,7 +2677,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (!data->immKbd->hIME || !data->immKbd->pImeGetImeMenuItems) return 0; + if (!data->immKbd->hIME) return 0; if (!is_himc_ime_unicode( data ) || (!parentA && !menuA)) ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); @@ -2741,7 +2737,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (!data->immKbd->hIME || !data->immKbd->pImeGetImeMenuItems) return 0; + if (!data->immKbd->hIME) return 0; if (is_himc_ime_unicode( data ) || (!parentW && !menuW)) ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); @@ -2933,7 +2929,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD if (!(data = get_imc_data( imc ))) return FALSE; - if (!data->immKbd->hIME || !data->immKbd->pImeToAsciiEx || data->lastVK == VK_PROCESSKEY) + if (!data->immKbd->hIME || data->lastVK == VK_PROCESSKEY) return FALSE; GetKeyboardState(state); @@ -3005,7 +3001,7 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD return FALSE; } - if (!data->immKbd->hIME || !data->immKbd->pImeProcessKey) + if (!data->immKbd->hIME) return FALSE; GetKeyboardState(state); From 730495f14d83201cf4107af3f2ae6b2fe202d303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 14:39:39 +0100 Subject: [PATCH 105/758] imm32: Return early if IMM_GetImmHkl fails. (cherry picked from commit 245465ba1ddb9b5ad1bd36df047547a332fb7c50) --- dlls/imm32/imm.c | 132 +++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 72 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e2c2f7bfccf..c09e51e9d6a 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -552,10 +552,8 @@ static struct ime *IMM_GetImmHkl( HKL hkl ) failed: if (ptr->hIME) FreeLibrary( ptr->hIME ); - ptr->hIME = NULL; - - list_add_head( &ImmHklList, &ptr->entry ); - return ptr; + HeapFree( GetProcessHeap(), 0, ptr ); + return NULL; } @@ -566,11 +564,8 @@ static void IMM_FreeAllImmHkl(void) LIST_FOR_EACH_ENTRY_SAFE( ptr, cursor2, &ImmHklList, struct ime, entry ) { list_remove(&ptr->entry); - if (ptr->hIME) - { - ptr->pImeDestroy(1); - FreeLibrary(ptr->hIME); - } + ptr->pImeDestroy( 1 ); + FreeLibrary( ptr->hIME ); if (ptr->UIWnd) DestroyWindow(ptr->UIWnd); HeapFree(GetProcessHeap(),0,ptr); @@ -680,8 +675,7 @@ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) if (data) { data->IMC.hWnd = activate ? hwnd : NULL; - - if (data->immKbd->hIME) data->immKbd->pImeSetActiveContext(himc, activate); + data->immKbd->pImeSetActiveContext( himc, activate ); } if (IsWindow(hwnd)) @@ -758,13 +752,13 @@ BOOL WINAPI ImmAssociateContextEx(HWND hwnd, HIMC imc, DWORD flags) */ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; BOOL ret; TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; - if (!ime->hIME) return FALSE; + if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; if (mode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); @@ -787,13 +781,13 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) */ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; BOOL ret; TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; - if (!ime->hIME) return FALSE; + if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; if (mode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); @@ -822,9 +816,7 @@ static InputContextData *create_input_context(HIMC default_imc) /* Load the IME */ new_context->threadDefault = !!default_imc; - new_context->immKbd = IMM_GetImmHkl(GetKeyboardLayout(0)); - - if (!new_context->immKbd->hIME) + if (!(new_context->immKbd = IMM_GetImmHkl( GetKeyboardLayout( 0 ) ))) { TRACE("IME dll could not be loaded\n"); HeapFree(GetProcessHeap(),0,new_context); @@ -921,13 +913,13 @@ BOOL WINAPI ImmDestroyContext(HIMC hIMC) UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const char *readingA, DWORD style, const char *stringA, void *user ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; UINT ret; TRACE( "hkl %p, procA %p, readingA %s, style %lu, stringA %s, user %p.\n", hkl, procA, debugstr_a(readingA), style, debugstr_a(stringA), user ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user ); @@ -948,13 +940,13 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WCHAR *readingW, DWORD style, const WCHAR *stringW, void *user ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; UINT ret; TRACE( "hkl %p, procW %p, readingW %s, style %lu, stringW %s, user %p.\n", hkl, procW, debugstr_w(readingW), style, debugstr_w(stringW), user ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user ); @@ -984,12 +976,12 @@ static inline BOOL EscapeRequiresWA(UINT uEscape) */ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; LRESULT ret; TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (!EscapeRequiresWA( code ) || !is_kbd_ime_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); @@ -1016,12 +1008,12 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) */ LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; LRESULT ret; TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (!EscapeRequiresWA( code ) || is_kbd_ime_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); @@ -1582,13 +1574,13 @@ HIMC WINAPI ImmGetContext(HWND hWnd) DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDIDATELIST *listA, DWORD lengthA, UINT flags ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; DWORD ret; TRACE( "hkl %p, himc %p, srcA %s, listA %p, lengthA %lu, flags %#x.\n", hkl, himc, debugstr_a(srcA), listA, lengthA, flags ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); @@ -1617,13 +1609,13 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDIDATELIST *listW, DWORD lengthW, UINT flags ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; DWORD ret; TRACE( "hkl %p, himc %p, srcW %s, listW %p, lengthW %lu, flags %#x.\n", hkl, himc, debugstr_w(srcW), listW, lengthW, flags ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); @@ -1857,8 +1849,7 @@ DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) TRACE( "hkl %p, index %lu.\n", hkl, index ); - ime = IMM_GetImmHkl( hkl ); - if (!ime || !ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; switch (index) { @@ -1880,12 +1871,12 @@ DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) */ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; UINT ret; TRACE( "hkl %p, count %u, styleA %p.\n", hkl, count, styleA ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleA ); @@ -1905,12 +1896,12 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) */ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; UINT ret; TRACE( "hkl %p, count %u, styleW %p.\n", hkl, count, styleW ); - if (!ime->hIME) return 0; + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleW ); @@ -2052,12 +2043,15 @@ HKL WINAPI ImmInstallIMEW( /*********************************************************************** * ImmIsIME (IMM32.@) */ -BOOL WINAPI ImmIsIME(HKL hKL) +BOOL WINAPI ImmIsIME( HKL hkl ) { - struct ime *ptr; - TRACE("(%p):\n", hKL); - ptr = IMM_GetImmHkl(hKL); - return (ptr && ptr->hIME); + struct ime *ime; + + TRACE( "hkl %p\n", hkl ); + + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + + return TRUE; } /*********************************************************************** @@ -2134,12 +2128,12 @@ BOOL WINAPI ImmNotifyIME( */ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const char *stringA ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; BOOL ret; TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); - if (!ime->hIME) return FALSE; + if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeRegisterWord( readingA, style, stringA ); @@ -2159,12 +2153,12 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const */ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const WCHAR *stringW ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; BOOL ret; TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); - if (!ime->hIME) return FALSE; + if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; if (is_kbd_ime_unicode( ime )) ret = ime->pImeRegisterWord( readingW, style, stringW ); @@ -2614,12 +2608,12 @@ BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID) */ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, const char *stringA ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; BOOL ret; TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); - if (!ime->hIME) return FALSE; + if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeUnregisterWord( readingA, style, stringA ); @@ -2639,12 +2633,12 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons */ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const WCHAR *stringW ) { - struct ime *ime = IMM_GetImmHkl( hkl ); + struct ime *ime; BOOL ret; TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); - if (!ime->hIME) return FALSE; + if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; if (is_kbd_ime_unicode( ime )) ret = ime->pImeUnregisterWord( readingW, style, stringW ); @@ -2677,8 +2671,6 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (!data->immKbd->hIME) return 0; - if (!is_himc_ime_unicode( data ) || (!parentA && !menuA)) ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); else @@ -2737,8 +2729,6 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (!data->immKbd->hIME) return 0; - if (is_himc_ime_unicode( data ) || (!parentW && !menuW)) ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); else @@ -2928,9 +2918,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD TRACE("%p %x %x %x\n",hwnd, msg, (UINT)wParam, (UINT)lKeyData); if (!(data = get_imc_data( imc ))) return FALSE; - - if (!data->immKbd->hIME || data->lastVK == VK_PROCESSKEY) - return FALSE; + if (data->lastVK == VK_PROCESSKEY) return FALSE; GetKeyboardState(state); scancode = lKeyData >> 0x10 & 0xff; @@ -2988,21 +2976,16 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD /* Make sure we are inputting to the correct keyboard */ if (data->immKbd->hkl != hKL) { - struct ime *new_hkl = IMM_GetImmHkl( hKL ); - if (new_hkl) - { - data->immKbd->pImeSelect(imc, FALSE); - data->immKbd->uSelected--; - data->immKbd = new_hkl; - data->immKbd->pImeSelect(imc, TRUE); - data->immKbd->uSelected++; - } - else - return FALSE; - } + struct ime *new_hkl; - if (!data->immKbd->hIME) - return FALSE; + if (!(new_hkl = IMM_GetImmHkl( hKL ))) return FALSE; + + data->immKbd->pImeSelect( imc, FALSE ); + data->immKbd->uSelected--; + data->immKbd = new_hkl; + data->immKbd->pImeSelect( imc, TRUE ); + data->immKbd->uSelected++; + } GetKeyboardState(state); if (data->immKbd->pImeProcessKey(imc, vKey, lKeyData, state)) @@ -3055,8 +3038,13 @@ BOOL WINAPI ImmDisableLegacyIME(void) static HWND get_ui_window(HKL hkl) { - struct ime *immHkl = IMM_GetImmHkl( hkl ); - return immHkl->UIWnd; + struct ime *ime; + HWND hwnd; + + if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + hwnd = ime->UIWnd; + + return hwnd; } static BOOL is_ime_ui_msg(UINT msg) From 45a03fc61bd32734a93d957cbb135bba5a957c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 17:03:45 +0100 Subject: [PATCH 106/758] imm32: Move IMM_FreeThreadData helper around. (cherry picked from commit 0b34236dfdc41f71b52398a15dc7e457bbf451ff) --- dlls/imm32/imm.c | 63 +++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index c09e51e9d6a..3b793b8e69d 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -421,39 +421,6 @@ static InputContextData *query_imc_data(HIMC handle) return ret && ret->handle == handle ? ret : NULL; } -static BOOL free_input_context_data(HIMC hIMC) -{ - InputContextData *data = query_imc_data(hIMC); - - if (!data) - return FALSE; - - TRACE("Destroying %p\n", hIMC); - - data->immKbd->uSelected--; - data->immKbd->pImeSelect(hIMC, FALSE); - SendMessageW(data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->immKbd); - - ImmDestroyIMCC(data->IMC.hCompStr); - ImmDestroyIMCC(data->IMC.hCandInfo); - ImmDestroyIMCC(data->IMC.hGuideLine); - ImmDestroyIMCC(data->IMC.hPrivate); - ImmDestroyIMCC(data->IMC.hMsgBuf); - - HeapFree(GetProcessHeap(), 0, data); - - return TRUE; -} - -static void IMM_FreeThreadData(void) -{ - struct coinit_spy *spy; - - free_input_context_data(UlongToHandle(NtUserGetThreadInfo()->default_imc)); - if ((spy = get_thread_coinit_spy())) - IInitializeSpy_Release(&spy->IInitializeSpy_iface); -} - static HMODULE load_graphics_driver(void) { static const WCHAR key_pathW[] = L"System\\CurrentControlSet\\Control\\Video\\{"; @@ -556,6 +523,36 @@ static struct ime *IMM_GetImmHkl( HKL hkl ) return NULL; } +static BOOL free_input_context_data( HIMC hIMC ) +{ + InputContextData *data = query_imc_data( hIMC ); + + if (!data) return FALSE; + + TRACE( "Destroying %p\n", hIMC ); + + data->immKbd->uSelected--; + data->immKbd->pImeSelect( hIMC, FALSE ); + SendMessageW( data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->immKbd ); + + ImmDestroyIMCC( data->IMC.hCompStr ); + ImmDestroyIMCC( data->IMC.hCandInfo ); + ImmDestroyIMCC( data->IMC.hGuideLine ); + ImmDestroyIMCC( data->IMC.hPrivate ); + ImmDestroyIMCC( data->IMC.hMsgBuf ); + + HeapFree( GetProcessHeap(), 0, data ); + + return TRUE; +} + +static void IMM_FreeThreadData(void) +{ + struct coinit_spy *spy; + + free_input_context_data( UlongToHandle( NtUserGetThreadInfo()->default_imc ) ); + if ((spy = get_thread_coinit_spy())) IInitializeSpy_Release( &spy->IInitializeSpy_iface ); +} static void IMM_FreeAllImmHkl(void) { From 6d1794abd705de0efe11c84fcee8b05b88a39b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 17:07:29 +0100 Subject: [PATCH 107/758] imm32: Rename input context immKbd to ime. (cherry picked from commit 4e7aa4fb6a9b890751c419d5f2416f6031bdf8b5) --- dlls/imm32/imm.c | 83 +++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 3b793b8e69d..94f8c328cce 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -91,7 +91,7 @@ typedef struct tagInputContextData INPUTCONTEXT IMC; DWORD threadID; - struct ime *immKbd; + struct ime *ime; UINT lastVK; BOOL threadDefault; } InputContextData; @@ -118,7 +118,7 @@ static const WCHAR szImeRegFmt[] = L"System\\CurrentControlSet\\Control\\Keyboar static inline BOOL is_himc_ime_unicode(const InputContextData *data) { - return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE); + return !!(data->ime->imeInfo.fdwProperty & IME_PROP_UNICODE); } static inline BOOL is_kbd_ime_unicode( const struct ime *hkl ) @@ -531,9 +531,9 @@ static BOOL free_input_context_data( HIMC hIMC ) TRACE( "Destroying %p\n", hIMC ); - data->immKbd->uSelected--; - data->immKbd->pImeSelect( hIMC, FALSE ); - SendMessageW( data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->immKbd ); + data->ime->uSelected--; + data->ime->pImeSelect( hIMC, FALSE ); + SendMessageW( data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->ime ); ImmDestroyIMCC( data->IMC.hCompStr ); ImmDestroyIMCC( data->IMC.hCandInfo ); @@ -672,7 +672,7 @@ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) if (data) { data->IMC.hWnd = activate ? hwnd : NULL; - data->immKbd->pImeSetActiveContext( himc, activate ); + data->ime->pImeSetActiveContext( himc, activate ); } if (IsWindow(hwnd)) @@ -813,7 +813,7 @@ static InputContextData *create_input_context(HIMC default_imc) /* Load the IME */ new_context->threadDefault = !!default_imc; - if (!(new_context->immKbd = IMM_GetImmHkl( GetKeyboardLayout( 0 ) ))) + if (!(new_context->ime = IMM_GetImmHkl( GetKeyboardLayout( 0 ) ))) { TRACE("IME dll could not be loaded\n"); HeapFree(GetProcessHeap(),0,new_context); @@ -838,10 +838,10 @@ static InputContextData *create_input_context(HIMC default_imc) new_context->IMC.cfCandForm[i].dwIndex = ~0u; /* Initialize the IME Private */ - new_context->IMC.hPrivate = ImmCreateIMCC(new_context->immKbd->imeInfo.dwPrivateDataSize); + new_context->IMC.hPrivate = ImmCreateIMCC( new_context->ime->imeInfo.dwPrivateDataSize ); - new_context->IMC.fdwConversion = new_context->immKbd->imeInfo.fdwConversionCaps; - new_context->IMC.fdwSentence = new_context->immKbd->imeInfo.fdwSentenceCaps; + new_context->IMC.fdwConversion = new_context->ime->imeInfo.fdwConversionCaps; + new_context->IMC.fdwSentence = new_context->ime->imeInfo.fdwSentenceCaps; if (!default_imc) new_context->handle = NtUserCreateInputContext((UINT_PTR)new_context); @@ -853,16 +853,16 @@ static InputContextData *create_input_context(HIMC default_imc) return 0; } - if (!new_context->immKbd->pImeSelect(new_context->handle, TRUE)) + if (!new_context->ime->pImeSelect( new_context->handle, TRUE )) { TRACE("Selection of IME failed\n"); IMM_DestroyContext(new_context); return 0; } new_context->threadID = GetCurrentThreadId(); - SendMessageW(GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->immKbd); + SendMessageW( GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->ime ); - new_context->immKbd->uSelected++; + new_context->ime->uSelected++; TRACE("Created context %p\n", new_context); return new_context; } @@ -2117,7 +2117,7 @@ BOOL WINAPI ImmNotifyIME( return FALSE; } - return data->immKbd->pNotifyIME(hIMC,dwAction,dwIndex,dwValue); + return data->ime->pNotifyIME( hIMC, dwAction, dwIndex, dwValue ); } /*********************************************************************** @@ -2326,9 +2326,8 @@ BOOL WINAPI ImmSetCompositionStringA( dwIndex == SCS_QUERYRECONVERTSTRING)) return FALSE; - if (!is_himc_ime_unicode(data)) - return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp, - dwCompLen, lpRead, dwReadLen); + if (!is_himc_ime_unicode( data )) + return data->ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0); if (comp_len) @@ -2384,9 +2383,8 @@ BOOL WINAPI ImmSetCompositionStringW( dwIndex == SCS_QUERYRECONVERTSTRING)) return FALSE; - if (is_himc_ime_unicode(data)) - return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp, - dwCompLen, lpRead, dwReadLen); + if (is_himc_ime_unicode( data )) + return data->ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL, NULL); @@ -2441,16 +2439,15 @@ BOOL WINAPI ImmSetCompositionWindow( data->IMC.cfCompForm = *lpCompForm; - if (IsWindowVisible(data->immKbd->UIWnd)) + if (IsWindowVisible( data->ime->UIWnd )) { reshow = TRUE; - ShowWindow(data->immKbd->UIWnd,SW_HIDE); + ShowWindow( data->ime->UIWnd, SW_HIDE ); } /* FIXME: this is a partial stub */ - if (reshow) - ShowWindow(data->immKbd->UIWnd,SW_SHOWNOACTIVATE); + if (reshow) ShowWindow( data->ime->UIWnd, SW_SHOWNOACTIVATE ); ImmInternalSendIMENotify(data, IMN_SETCOMPOSITIONWINDOW, 0); return TRUE; @@ -2512,16 +2509,14 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) if (IMM_IsCrossThreadAccess(NULL, hIMC)) return FALSE; - if (data->immKbd->UIWnd == NULL) + if (data->ime->UIWnd == NULL) { /* create the ime window */ - data->immKbd->UIWnd = CreateWindowExW( WS_EX_TOOLWINDOW, - data->immKbd->imeClassName, NULL, WS_POPUP, 0, 0, 1, 1, 0, - 0, data->immKbd->hIME, 0); - SetWindowLongPtrW(data->immKbd->UIWnd, IMMGWL_IMC, (LONG_PTR)data); + data->ime->UIWnd = CreateWindowExW( WS_EX_TOOLWINDOW, data->ime->imeClassName, NULL, + WS_POPUP, 0, 0, 1, 1, 0, 0, data->ime->hIME, 0 ); + SetWindowLongPtrW( data->ime->UIWnd, IMMGWL_IMC, (LONG_PTR)data ); } - else if (fOpen) - SetWindowLongPtrW(data->immKbd->UIWnd, IMMGWL_IMC, (LONG_PTR)data); + else if (fOpen) SetWindowLongPtrW( data->ime->UIWnd, IMMGWL_IMC, (LONG_PTR)data ); if (!fOpen != !data->IMC.fOpen) { @@ -2669,7 +2664,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE } if (!is_himc_ime_unicode( data ) || (!parentA && !menuA)) - ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); + ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); else { IMEMENUITEMINFOW tmpW, *menuW, *parentW = parentA ? &tmpW : NULL; @@ -2682,7 +2677,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE menuW = HeapAlloc( GetProcessHeap(), 0, size ); } - ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); + ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); if (parentA) { @@ -2727,7 +2722,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE } if (is_himc_ime_unicode( data ) || (!parentW && !menuW)) - ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); + ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); else { IMEMENUITEMINFOA tmpA, *menuA, *parentA = parentW ? &tmpA : NULL; @@ -2740,7 +2735,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE menuA = HeapAlloc( GetProcessHeap(), 0, size ); } - ret = data->immKbd->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); + ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); if (parentW) { @@ -2923,7 +2918,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list_count * sizeof(TRANSMSG) + sizeof(DWORD)); list->uMsgCount = list_count; - if (data->immKbd->imeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST) + if (data->ime->imeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { WCHAR chr; @@ -2936,7 +2931,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD else uVirtKey = data->lastVK; - msg_count = data->immKbd->pImeToAsciiEx(uVirtKey, scancode, state, list, 0, imc); + msg_count = data->ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, imc ); TRACE("%i messages generated\n",msg_count); if (msg_count && msg_count <= list_count) { @@ -2971,21 +2966,21 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD if (!(data = get_imc_data( imc ))) return FALSE; /* Make sure we are inputting to the correct keyboard */ - if (data->immKbd->hkl != hKL) + if (data->ime->hkl != hKL) { struct ime *new_hkl; if (!(new_hkl = IMM_GetImmHkl( hKL ))) return FALSE; - data->immKbd->pImeSelect( imc, FALSE ); - data->immKbd->uSelected--; - data->immKbd = new_hkl; - data->immKbd->pImeSelect( imc, TRUE ); - data->immKbd->uSelected++; + data->ime->pImeSelect( imc, FALSE ); + data->ime->uSelected--; + data->ime = new_hkl; + data->ime->pImeSelect( imc, TRUE ); + data->ime->uSelected++; } GetKeyboardState(state); - if (data->immKbd->pImeProcessKey(imc, vKey, lKeyData, state)) + if (data->ime->pImeProcessKey( imc, vKey, lKeyData, state )) { data->lastVK = vKey; return TRUE; From fd3fffc52702b9f4b115e5750af0d30d81fc2385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 11:15:16 +0100 Subject: [PATCH 108/758] imm32: Implement ImmLoadIME and ImmFreeLayout. (cherry picked from commit fa9de19a66261cdb979ec131ff14e391ddce095b) --- dlls/imm32/imm.c | 223 +++++++++++++++++++++++++++------------ dlls/imm32/tests/imm32.c | 18 +--- 2 files changed, 154 insertions(+), 87 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 94f8c328cce..389aead8264 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -54,9 +54,12 @@ static UINT WM_MSIME_DOCUMENTFEED; struct ime { + LONG refcount; /* guarded by ime_cs */ + + HKL hkl; + HMODULE module; struct list entry; - HKL hkl; - HMODULE hIME; + IMEINFO imeInfo; WCHAR imeClassName[17]; /* 16 character max */ ULONG uSelected; @@ -112,7 +115,15 @@ struct coinit_spy } apt_flags; }; -static struct list ImmHklList = LIST_INIT(ImmHklList); +static CRITICAL_SECTION ime_cs; +static CRITICAL_SECTION_DEBUG ime_cs_debug = +{ + 0, 0, &ime_cs, + { &ime_cs_debug.ProcessLocksList, &ime_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": ime_cs") } +}; +static CRITICAL_SECTION ime_cs = { &ime_cs_debug, -1, 0, 0, 0, 0 }; +static struct list ime_list = LIST_INIT( ime_list ); static const WCHAR szImeRegFmt[] = L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lx"; @@ -446,45 +457,62 @@ static HMODULE load_graphics_driver(void) return ret; } -BOOL WINAPI ImmFreeLayout( HKL hkl ) +/* lookup an IME from a HKL, must hold ime_cs */ +static struct ime *find_ime_from_hkl( HKL hkl ) { - FIXME( "hkl %p stub!\n", hkl ); - return FALSE; + struct ime *ime = NULL; + LIST_FOR_EACH_ENTRY( ime, &ime_list, struct ime, entry ) + if (ime->hkl == hkl) return ime; + return NULL; } -BOOL WINAPI ImmLoadIME( HKL hkl ) +BOOL WINAPI ImmFreeLayout( HKL hkl ) { - FIXME( "hkl %p stub!\n", hkl ); - return FALSE; + struct ime *ime; + + TRACE( "hkl %p\n", hkl ); + + EnterCriticalSection( &ime_cs ); + if ((ime = find_ime_from_hkl( hkl )) && ime->refcount) ime = NULL; + if (ime) list_remove( &ime->entry ); + LeaveCriticalSection( &ime_cs ); + if (!ime) return TRUE; + + if (!ime->pImeDestroy( 0 )) WARN( "ImeDestroy failed\n" ); + FreeLibrary( ime->module ); + free( ime ); + return TRUE; } -/* struct ime loading and freeing */ -static struct ime *IMM_GetImmHkl( HKL hkl ) +BOOL WINAPI ImmLoadIME( HKL hkl ) { - struct ime *ptr; - WCHAR filename[MAX_PATH]; + WCHAR buffer[MAX_PATH] = {0}; + struct ime *ime; - TRACE("Seeking ime for keyboard %p\n",hkl); + TRACE( "hkl %p\n", hkl ); - LIST_FOR_EACH_ENTRY( ptr, &ImmHklList, struct ime, entry ) - { - if (ptr->hkl == hkl) - return ptr; - } - /* not found... create it */ + EnterCriticalSection( &ime_cs ); + ime = find_ime_from_hkl( hkl ); + LeaveCriticalSection( &ime_cs ); + if (ime) return TRUE; - ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ime) ); + if (!(ime = calloc( 1, sizeof(*ime) ))) return FALSE; + ime->hkl = hkl; - ptr->hkl = hkl; - if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename); - if (!ptr->hIME) ptr->hIME = load_graphics_driver(); - if (!ptr->hIME) goto failed; + if (!ImmGetIMEFileNameW( hkl, buffer, MAX_PATH )) ime->module = NULL; + else ime->module = LoadLibraryW( buffer ); + + if (!ime->module) + { + if (*buffer) WARN( "Failed to load %s, falling back to default.\n", debugstr_w(buffer) ); + if (!(ime->module = load_graphics_driver())) goto failed; + } -#define LOAD_FUNCPTR( f ) \ - if (!(ptr->p##f = (void *)GetProcAddress( ptr->hIME, #f ))) \ - { \ - WARN( "Can't find function %s in ime\n", #f ); \ - goto failed; \ +#define LOAD_FUNCPTR( f ) \ + if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f ))) \ + { \ + WARN( "Can't find function %s in HKL %p IME\n", #f, hkl ); \ + goto failed; \ } LOAD_FUNCPTR( ImeInquire ); LOAD_FUNCPTR( ImeDestroy ); @@ -504,23 +532,57 @@ static struct ime *IMM_GetImmHkl( HKL hkl ) LOAD_FUNCPTR( ImeGetImeMenuItems ); #undef LOAD_FUNCPTR - if (!ptr->pImeInquire( &ptr->imeInfo, ptr->imeClassName, 0 )) goto failed; + if (!ime->pImeInquire( &ime->imeInfo, buffer, 0 )) goto failed; - /* make sure our classname is WCHAR */ - if (!is_kbd_ime_unicode( ptr )) + if (is_kbd_ime_unicode( ime )) lstrcpynW( ime->imeClassName, buffer, ARRAY_SIZE(ime->imeClassName) ); + else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->imeClassName, ARRAY_SIZE(ime->imeClassName) ); + + EnterCriticalSection( &ime_cs ); + list_add_tail( &ime_list, &ime->entry ); + LeaveCriticalSection( &ime_cs ); + + TRACE( "Created IME %p for HKL %p\n", ime, hkl ); + return TRUE; + +failed: + if (ime->module) FreeLibrary( ime->module ); + free( ime ); + return FALSE; +} + +static struct ime *ime_acquire( HKL hkl ) +{ + struct ime *ime; + + EnterCriticalSection( &ime_cs ); + + if (!ImmLoadIME( hkl )) ime = NULL; + else ime = find_ime_from_hkl( hkl ); + + if (ime) { - WCHAR bufW[17]; - MultiByteToWideChar( CP_ACP, 0, (LPSTR)ptr->imeClassName, -1, bufW, 17 ); - lstrcpyW( ptr->imeClassName, bufW ); + ULONG ref = ++ime->refcount; + TRACE( "ime %p increasing refcount to %lu.\n", ime, ref ); } - list_add_head(&ImmHklList,&ptr->entry); - return ptr; + LeaveCriticalSection( &ime_cs ); -failed: - if (ptr->hIME) FreeLibrary( ptr->hIME ); - HeapFree( GetProcessHeap(), 0, ptr ); - return NULL; + return ime; +} + +static void ime_release( struct ime *ime ) +{ + ULONG ref; + + EnterCriticalSection( &ime_cs ); + + ref = --ime->refcount; + TRACE( "ime %p decreasing refcount to %lu.\n", ime, ref ); + + if (!ref && (ime->imeInfo.fdwProperty & IME_PROP_END_UNLOAD)) + ImmFreeLayout( ime->hkl ); + + LeaveCriticalSection( &ime_cs ); } static BOOL free_input_context_data( HIMC hIMC ) @@ -541,6 +603,7 @@ static BOOL free_input_context_data( HIMC hIMC ) ImmDestroyIMCC( data->IMC.hPrivate ); ImmDestroyIMCC( data->IMC.hMsgBuf ); + ime_release( data->ime ); HeapFree( GetProcessHeap(), 0, data ); return TRUE; @@ -556,16 +619,17 @@ static void IMM_FreeThreadData(void) static void IMM_FreeAllImmHkl(void) { - struct ime *ptr, *cursor2; + struct ime *ime, *next; - LIST_FOR_EACH_ENTRY_SAFE( ptr, cursor2, &ImmHklList, struct ime, entry ) + LIST_FOR_EACH_ENTRY_SAFE( ime, next, &ime_list, struct ime, entry ) { - list_remove(&ptr->entry); - ptr->pImeDestroy( 1 ); - FreeLibrary( ptr->hIME ); - if (ptr->UIWnd) - DestroyWindow(ptr->UIWnd); - HeapFree(GetProcessHeap(),0,ptr); + list_remove( &ime->entry ); + + ime->pImeDestroy( 1 ); + FreeLibrary( ime->module ); + + if (ime->UIWnd) DestroyWindow( ime->UIWnd ); + free( ime ); } } @@ -755,7 +819,7 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; - if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; + if (!(ime = ime_acquire( hkl ))) return FALSE; if (mode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); @@ -770,6 +834,7 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) HeapFree( GetProcessHeap(), 0, wordW.lpWord ); } + ime_release( ime ); return ret; } @@ -784,7 +849,7 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data ); if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; - if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; + if (!(ime = ime_acquire( hkl ))) return FALSE; if (mode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); @@ -799,6 +864,7 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) HeapFree( GetProcessHeap(), 0, wordA.lpWord ); } + ime_release( ime ); return ret; } @@ -813,7 +879,7 @@ static InputContextData *create_input_context(HIMC default_imc) /* Load the IME */ new_context->threadDefault = !!default_imc; - if (!(new_context->ime = IMM_GetImmHkl( GetKeyboardLayout( 0 ) ))) + if (!(new_context->ime = ime_acquire( GetKeyboardLayout( 0 ) ))) { TRACE("IME dll could not be loaded\n"); HeapFree(GetProcessHeap(),0,new_context); @@ -916,7 +982,7 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch TRACE( "hkl %p, procA %p, readingA %s, style %lu, stringA %s, user %p.\n", hkl, procA, debugstr_a(readingA), style, debugstr_a(stringA), user ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user ); @@ -928,6 +994,7 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch HeapFree( GetProcessHeap(), 0, stringW ); } + ime_release( ime ); return ret; } @@ -943,7 +1010,7 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC TRACE( "hkl %p, procW %p, readingW %s, style %lu, stringW %s, user %p.\n", hkl, procW, debugstr_w(readingW), style, debugstr_w(stringW), user ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user ); @@ -955,6 +1022,7 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC HeapFree( GetProcessHeap(), 0, stringA ); } + ime_release( ime ); return ret; } @@ -978,7 +1046,7 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (!EscapeRequiresWA( code ) || !is_kbd_ime_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); @@ -997,6 +1065,7 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) } } + ime_release( ime ); return ret; } @@ -1010,7 +1079,7 @@ LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (!EscapeRequiresWA( code ) || is_kbd_ime_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); @@ -1029,6 +1098,7 @@ LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) } } + ime_release( ime ); return ret; } @@ -1577,7 +1647,7 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID TRACE( "hkl %p, himc %p, srcA %s, listA %p, lengthA %lu, flags %#x.\n", hkl, himc, debugstr_a(srcA), listA, lengthA, flags ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); @@ -1597,6 +1667,7 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID HeapFree( GetProcessHeap(), 0, srcW ); } + ime_release( ime ); return ret; } @@ -1612,7 +1683,7 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI TRACE( "hkl %p, himc %p, srcW %s, listW %p, lengthW %lu, flags %#x.\n", hkl, himc, debugstr_w(srcW), listW, lengthW, flags ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); @@ -1632,6 +1703,7 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI HeapFree( GetProcessHeap(), 0, srcA ); } + ime_release( ime ); return ret; } @@ -1846,7 +1918,7 @@ DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) TRACE( "hkl %p, index %lu.\n", hkl, index ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; switch (index) { @@ -1860,6 +1932,7 @@ DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) default: ret = 0; break; } + ime_release( ime ); return ret; } @@ -1873,7 +1946,7 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) TRACE( "hkl %p, count %u, styleA %p.\n", hkl, count, styleA ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleA ); @@ -1885,6 +1958,7 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) styleA->dwStyle = styleW.dwStyle; } + ime_release( ime ); return ret; } @@ -1898,7 +1972,7 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) TRACE( "hkl %p, count %u, styleW %p.\n", hkl, count, styleW ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; if (is_kbd_ime_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleW ); @@ -1910,6 +1984,7 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) styleW->dwStyle = styleA.dwStyle; } + ime_release( ime ); return ret; } @@ -2046,7 +2121,8 @@ BOOL WINAPI ImmIsIME( HKL hkl ) TRACE( "hkl %p\n", hkl ); - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; + ime_release( ime ); return TRUE; } @@ -2130,7 +2206,7 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); - if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; + if (!(ime = ime_acquire( hkl ))) return FALSE; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeRegisterWord( readingA, style, stringA ); @@ -2142,6 +2218,7 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const HeapFree( GetProcessHeap(), 0, stringW ); } + ime_release( ime ); return ret; } @@ -2155,7 +2232,7 @@ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); - if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; + if (!(ime = ime_acquire( hkl ))) return FALSE; if (is_kbd_ime_unicode( ime )) ret = ime->pImeRegisterWord( readingW, style, stringW ); @@ -2167,6 +2244,7 @@ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const HeapFree( GetProcessHeap(), 0, stringA ); } + ime_release( ime ); return ret; } @@ -2513,7 +2591,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) { /* create the ime window */ data->ime->UIWnd = CreateWindowExW( WS_EX_TOOLWINDOW, data->ime->imeClassName, NULL, - WS_POPUP, 0, 0, 1, 1, 0, 0, data->ime->hIME, 0 ); + WS_POPUP, 0, 0, 1, 1, 0, 0, data->ime->module, 0 ); SetWindowLongPtrW( data->ime->UIWnd, IMMGWL_IMC, (LONG_PTR)data ); } else if (fOpen) SetWindowLongPtrW( data->ime->UIWnd, IMMGWL_IMC, (LONG_PTR)data ); @@ -2605,7 +2683,7 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) ); - if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; + if (!(ime = ime_acquire( hkl ))) return FALSE; if (!is_kbd_ime_unicode( ime )) ret = ime->pImeUnregisterWord( readingA, style, stringA ); @@ -2617,6 +2695,7 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons HeapFree( GetProcessHeap(), 0, stringW ); } + ime_release( ime ); return ret; } @@ -2630,7 +2709,7 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) ); - if (!(ime = IMM_GetImmHkl( hkl ))) return FALSE; + if (!(ime = ime_acquire( hkl ))) return FALSE; if (is_kbd_ime_unicode( ime )) ret = ime->pImeUnregisterWord( readingW, style, stringW ); @@ -2642,6 +2721,7 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con HeapFree( GetProcessHeap(), 0, stringA ); } + ime_release( ime ); return ret; } @@ -2970,10 +3050,12 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD { struct ime *new_hkl; - if (!(new_hkl = IMM_GetImmHkl( hKL ))) return FALSE; + if (!(new_hkl = ime_acquire( hKL ))) return FALSE; data->ime->pImeSelect( imc, FALSE ); data->ime->uSelected--; + ime_release( data->ime ); + data->ime = new_hkl; data->ime->pImeSelect( imc, TRUE ); data->ime->uSelected++; @@ -3033,8 +3115,9 @@ static HWND get_ui_window(HKL hkl) struct ime *ime; HWND hwnd; - if (!(ime = IMM_GetImmHkl( hkl ))) return 0; + if (!(ime = ime_acquire( hkl ))) return 0; hwnd = ime->UIWnd; + ime_release( ime ); return hwnd; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 9d63c80c514..16e570c4c8d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2603,7 +2603,7 @@ static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserve case DLL_PROCESS_DETACH: UnregisterClassW( ime_ui_class.lpszClassName, instance ); - todo_wine CHECK_EXPECT( IME_DLL_PROCESS_DETACH ); + CHECK_EXPECT( IME_DLL_PROCESS_DETACH ); break; } @@ -2770,29 +2770,21 @@ static void test_ImmInstallIME(void) SET_EXPECT( IME_DLL_PROCESS_ATTACH ); SET_EXPECT( ImeInquire ); ret = ImmLoadIME( hkl ); - todo_wine ok( ret, "ImmLoadIME returned %#x\n", ret ); - todo_wine CHECK_CALLED( IME_DLL_PROCESS_ATTACH ); - todo_wine CHECK_CALLED( ImeInquire ); ret = ImmLoadIME( hkl ); - todo_wine ok( ret, "ImmLoadIME returned %#x\n", ret ); SET_EXPECT( ImeDestroy ); SET_EXPECT( IME_DLL_PROCESS_DETACH ); ret = ImmFreeLayout( hkl ); - todo_wine ok( ret, "ImmFreeLayout returned %#x\n", ret ); - todo_wine CHECK_CALLED( ImeDestroy ); - todo_wine CHECK_CALLED( IME_DLL_PROCESS_DETACH ); ret = ImmFreeLayout( hkl ); - todo_wine ok( ret, "ImmFreeLayout returned %#x\n", ret ); ime_cleanup( hkl ); @@ -2804,29 +2796,21 @@ static void test_ImmInstallIME(void) SET_EXPECT( IME_DLL_PROCESS_ATTACH ); SET_EXPECT( ImeInquire ); ret = ImmLoadIME( hkl ); - todo_wine ok( ret, "ImmLoadIME returned %#x\n", ret ); - todo_wine CHECK_CALLED( IME_DLL_PROCESS_ATTACH ); - todo_wine CHECK_CALLED( ImeInquire ); ret = ImmLoadIME( hkl ); - todo_wine ok( ret, "ImmLoadIME returned %#x\n", ret ); SET_EXPECT( ImeDestroy ); SET_EXPECT( IME_DLL_PROCESS_DETACH ); ret = ImmFreeLayout( hkl ); - todo_wine ok( ret, "ImmFreeLayout returned %#x\n", ret ); - todo_wine CHECK_CALLED( ImeDestroy ); - todo_wine CHECK_CALLED( IME_DLL_PROCESS_DETACH ); ret = ImmFreeLayout( hkl ); - todo_wine ok( ret, "ImmFreeLayout returned %#x\n", ret ); ime_cleanup( hkl ); From 220656a019e38c8d3b63c15a3066bdb46ba4344c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 17:43:58 +0100 Subject: [PATCH 109/758] imm32: Rename some struct ime members. (cherry picked from commit c76b55991ac57d821be2cbd7f62121bf38157956) --- dlls/imm32/imm.c | 66 ++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 389aead8264..e0366961767 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -60,12 +60,12 @@ struct ime HMODULE module; struct list entry; - IMEINFO imeInfo; - WCHAR imeClassName[17]; /* 16 character max */ - ULONG uSelected; - HWND UIWnd; + IMEINFO info; + WCHAR ui_class[17]; + HWND ui_hwnd; + + ULONG uSelected; - /* Function Pointers */ BOOL (WINAPI *pImeInquire)(IMEINFO *, void *, DWORD); BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *); BOOL (WINAPI *pImeDestroy)(UINT); @@ -129,12 +129,12 @@ static const WCHAR szImeRegFmt[] = L"System\\CurrentControlSet\\Control\\Keyboar static inline BOOL is_himc_ime_unicode(const InputContextData *data) { - return !!(data->ime->imeInfo.fdwProperty & IME_PROP_UNICODE); + return !!(data->ime->info.fdwProperty & IME_PROP_UNICODE); } static inline BOOL is_kbd_ime_unicode( const struct ime *hkl ) { - return !!(hkl->imeInfo.fdwProperty & IME_PROP_UNICODE); + return !!(hkl->info.fdwProperty & IME_PROP_UNICODE); } static BOOL IMM_DestroyContext(HIMC hIMC); @@ -532,10 +532,10 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) LOAD_FUNCPTR( ImeGetImeMenuItems ); #undef LOAD_FUNCPTR - if (!ime->pImeInquire( &ime->imeInfo, buffer, 0 )) goto failed; + if (!ime->pImeInquire( &ime->info, buffer, 0 )) goto failed; - if (is_kbd_ime_unicode( ime )) lstrcpynW( ime->imeClassName, buffer, ARRAY_SIZE(ime->imeClassName) ); - else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->imeClassName, ARRAY_SIZE(ime->imeClassName) ); + if (is_kbd_ime_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) ); + else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->ui_class, ARRAY_SIZE(ime->ui_class) ); EnterCriticalSection( &ime_cs ); list_add_tail( &ime_list, &ime->entry ); @@ -579,7 +579,7 @@ static void ime_release( struct ime *ime ) ref = --ime->refcount; TRACE( "ime %p decreasing refcount to %lu.\n", ime, ref ); - if (!ref && (ime->imeInfo.fdwProperty & IME_PROP_END_UNLOAD)) + if (!ref && (ime->info.fdwProperty & IME_PROP_END_UNLOAD)) ImmFreeLayout( ime->hkl ); LeaveCriticalSection( &ime_cs ); @@ -628,7 +628,7 @@ static void IMM_FreeAllImmHkl(void) ime->pImeDestroy( 1 ); FreeLibrary( ime->module ); - if (ime->UIWnd) DestroyWindow( ime->UIWnd ); + if (ime->ui_hwnd) DestroyWindow( ime->ui_hwnd ); free( ime ); } } @@ -904,10 +904,10 @@ static InputContextData *create_input_context(HIMC default_imc) new_context->IMC.cfCandForm[i].dwIndex = ~0u; /* Initialize the IME Private */ - new_context->IMC.hPrivate = ImmCreateIMCC( new_context->ime->imeInfo.dwPrivateDataSize ); + new_context->IMC.hPrivate = ImmCreateIMCC( new_context->ime->info.dwPrivateDataSize ); - new_context->IMC.fdwConversion = new_context->ime->imeInfo.fdwConversionCaps; - new_context->IMC.fdwSentence = new_context->ime->imeInfo.fdwSentenceCaps; + new_context->IMC.fdwConversion = new_context->ime->info.fdwConversionCaps; + new_context->IMC.fdwSentence = new_context->ime->info.fdwSentenceCaps; if (!default_imc) new_context->handle = NtUserCreateInputContext((UINT_PTR)new_context); @@ -1922,11 +1922,11 @@ DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index ) switch (index) { - case IGP_PROPERTY: ret = ime->imeInfo.fdwProperty; break; - case IGP_CONVERSION: ret = ime->imeInfo.fdwConversionCaps; break; - case IGP_SENTENCE: ret = ime->imeInfo.fdwSentenceCaps; break; - case IGP_SETCOMPSTR: ret = ime->imeInfo.fdwSCSCaps; break; - case IGP_SELECT: ret = ime->imeInfo.fdwSelectCaps; break; + case IGP_PROPERTY: ret = ime->info.fdwProperty; break; + case IGP_CONVERSION: ret = ime->info.fdwConversionCaps; break; + case IGP_SENTENCE: ret = ime->info.fdwSentenceCaps; break; + case IGP_SETCOMPSTR: ret = ime->info.fdwSCSCaps; break; + case IGP_SELECT: ret = ime->info.fdwSelectCaps; break; case IGP_GETIMEVERSION: ret = IMEVER_0400; break; case IGP_UI: ret = 0; break; default: ret = 0; break; @@ -2517,15 +2517,15 @@ BOOL WINAPI ImmSetCompositionWindow( data->IMC.cfCompForm = *lpCompForm; - if (IsWindowVisible( data->ime->UIWnd )) + if (IsWindowVisible( data->ime->ui_hwnd )) { reshow = TRUE; - ShowWindow( data->ime->UIWnd, SW_HIDE ); + ShowWindow( data->ime->ui_hwnd, SW_HIDE ); } /* FIXME: this is a partial stub */ - if (reshow) ShowWindow( data->ime->UIWnd, SW_SHOWNOACTIVATE ); + if (reshow) ShowWindow( data->ime->ui_hwnd, SW_SHOWNOACTIVATE ); ImmInternalSendIMENotify(data, IMN_SETCOMPOSITIONWINDOW, 0); return TRUE; @@ -2587,14 +2587,14 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) if (IMM_IsCrossThreadAccess(NULL, hIMC)) return FALSE; - if (data->ime->UIWnd == NULL) + if (data->ime->ui_hwnd == NULL) { /* create the ime window */ - data->ime->UIWnd = CreateWindowExW( WS_EX_TOOLWINDOW, data->ime->imeClassName, NULL, + data->ime->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, data->ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, 0, 0, data->ime->module, 0 ); - SetWindowLongPtrW( data->ime->UIWnd, IMMGWL_IMC, (LONG_PTR)data ); + SetWindowLongPtrW( data->ime->ui_hwnd, IMMGWL_IMC, (LONG_PTR)data ); } - else if (fOpen) SetWindowLongPtrW( data->ime->UIWnd, IMMGWL_IMC, (LONG_PTR)data ); + else if (fOpen) SetWindowLongPtrW( data->ime->ui_hwnd, IMMGWL_IMC, (LONG_PTR)data ); if (!fOpen != !data->IMC.fOpen) { @@ -2998,7 +2998,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list_count * sizeof(TRANSMSG) + sizeof(DWORD)); list->uMsgCount = list_count; - if (data->ime->imeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST) + if (data->ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { WCHAR chr; @@ -3116,7 +3116,7 @@ static HWND get_ui_window(HKL hkl) HWND hwnd; if (!(ime = ime_acquire( hkl ))) return 0; - hwnd = ime->UIWnd; + hwnd = ime->ui_hwnd; ime_release( ime ); return hwnd; @@ -3189,7 +3189,7 @@ static void init_messages(void) LRESULT WINAPI __wine_ime_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi) { - HWND uiwnd; + HWND ui_hwnd; switch (msg) { @@ -3211,12 +3211,12 @@ LRESULT WINAPI __wine_ime_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp if (is_ime_ui_msg(msg)) { - if ((uiwnd = get_ui_window(NtUserGetKeyboardLayout(0)))) + if ((ui_hwnd = get_ui_window(NtUserGetKeyboardLayout(0)))) { if (ansi) - return SendMessageA(uiwnd, msg, wparam, lparam); + return SendMessageA(ui_hwnd, msg, wparam, lparam); else - return SendMessageW(uiwnd, msg, wparam, lparam); + return SendMessageW(ui_hwnd, msg, wparam, lparam); } return FALSE; } From 7ae04a71d5381a39983bbd0aeaf0d3ec18967832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 17:44:44 +0100 Subject: [PATCH 110/758] imm32: Delete unnecessary uSelected struct ime member. (cherry picked from commit ea4b97bf4ec332f263c6aae341daba37506e7ffb) --- dlls/imm32/imm.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e0366961767..695e263d39b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -64,8 +64,6 @@ struct ime WCHAR ui_class[17]; HWND ui_hwnd; - ULONG uSelected; - BOOL (WINAPI *pImeInquire)(IMEINFO *, void *, DWORD); BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *); BOOL (WINAPI *pImeDestroy)(UINT); @@ -593,7 +591,6 @@ static BOOL free_input_context_data( HIMC hIMC ) TRACE( "Destroying %p\n", hIMC ); - data->ime->uSelected--; data->ime->pImeSelect( hIMC, FALSE ); SendMessageW( data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->ime ); @@ -928,7 +925,6 @@ static InputContextData *create_input_context(HIMC default_imc) new_context->threadID = GetCurrentThreadId(); SendMessageW( GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->ime ); - new_context->ime->uSelected++; TRACE("Created context %p\n", new_context); return new_context; } @@ -3053,12 +3049,10 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD if (!(new_hkl = ime_acquire( hKL ))) return FALSE; data->ime->pImeSelect( imc, FALSE ); - data->ime->uSelected--; ime_release( data->ime ); data->ime = new_hkl; data->ime->pImeSelect( imc, TRUE ); - data->ime->uSelected++; } GetKeyboardState(state); From 56166c42605b30dfd0645c5042bba12e5e63bfbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 17:47:29 +0100 Subject: [PATCH 111/758] imm32: Use a single ime_is_unicode helper. (cherry picked from commit 0392621858a46d7a282b3585e6cad2333a4ca61c) --- dlls/imm32/imm.c | 73 ++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 695e263d39b..14b357d9533 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -125,14 +125,9 @@ static struct list ime_list = LIST_INIT( ime_list ); static const WCHAR szImeRegFmt[] = L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lx"; -static inline BOOL is_himc_ime_unicode(const InputContextData *data) +static BOOL ime_is_unicode( const struct ime *ime ) { - return !!(data->ime->info.fdwProperty & IME_PROP_UNICODE); -} - -static inline BOOL is_kbd_ime_unicode( const struct ime *hkl ) -{ - return !!(hkl->info.fdwProperty & IME_PROP_UNICODE); + return !!(ime->info.fdwProperty & IME_PROP_UNICODE); } static BOOL IMM_DestroyContext(HIMC hIMC); @@ -532,7 +527,7 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) if (!ime->pImeInquire( &ime->info, buffer, 0 )) goto failed; - if (is_kbd_ime_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) ); + if (ime_is_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) ); else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->ui_class, ARRAY_SIZE(ime->ui_class) ); EnterCriticalSection( &ime_cs ); @@ -818,7 +813,7 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; if (!(ime = ime_acquire( hkl ))) return FALSE; - if (mode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode( ime )) + if (mode != IME_CONFIG_REGISTERWORD || !ime_is_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); else { @@ -848,7 +843,7 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE; if (!(ime = ime_acquire( hkl ))) return FALSE; - if (mode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode( ime )) + if (mode != IME_CONFIG_REGISTERWORD || ime_is_unicode( ime )) ret = ime->pImeConfigure( hkl, hwnd, mode, data ); else { @@ -980,7 +975,7 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch if (!(ime = ime_acquire( hkl ))) return 0; - if (!is_kbd_ime_unicode( ime )) + if (!ime_is_unicode( ime )) ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user ); else { @@ -1008,7 +1003,7 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC if (!(ime = ime_acquire( hkl ))) return 0; - if (is_kbd_ime_unicode( ime )) + if (ime_is_unicode( ime )) ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user ); else { @@ -1044,7 +1039,7 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) if (!(ime = ime_acquire( hkl ))) return 0; - if (!EscapeRequiresWA( code ) || !is_kbd_ime_unicode( ime )) + if (!EscapeRequiresWA( code ) || !ime_is_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); else { @@ -1077,7 +1072,7 @@ LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) if (!(ime = ime_acquire( hkl ))) return 0; - if (!EscapeRequiresWA( code ) || is_kbd_ime_unicode( ime )) + if (!EscapeRequiresWA( code ) || ime_is_unicode( ime )) ret = ime->pImeEscape( himc, code, data ); else { @@ -1123,7 +1118,7 @@ DWORD WINAPI ImmGetCandidateListA( if ( !candlist->dwSize || !candlist->dwCount ) goto done; - if ( !is_himc_ime_unicode(data) ) + if (!ime_is_unicode( data->ime )) { ret = candlist->dwSize; if ( lpCandList && dwBufLen >= ret ) @@ -1156,7 +1151,7 @@ DWORD WINAPI ImmGetCandidateListCountA( *lpdwListCount = count = candinfo->dwCount; - if ( !is_himc_ime_unicode(data) ) + if (!ime_is_unicode( data->ime )) ret = candinfo->dwSize; else { @@ -1188,7 +1183,7 @@ DWORD WINAPI ImmGetCandidateListCountW( *lpdwListCount = count = candinfo->dwCount; - if ( is_himc_ime_unicode(data) ) + if (ime_is_unicode( data->ime )) ret = candinfo->dwSize; else { @@ -1226,7 +1221,7 @@ DWORD WINAPI ImmGetCandidateListW( if ( !candlist->dwSize || !candlist->dwCount ) goto done; - if ( is_himc_ime_unicode(data) ) + if (ime_is_unicode( data->ime )) { ret = candlist->dwSize; if ( lpCandList && dwBufLen >= ret ) @@ -1312,7 +1307,7 @@ static INT CopyCompStringIMEtoClient(const InputContextData *data, const void *s int char_size = unicode ? sizeof(WCHAR) : sizeof(char); INT ret; - if (is_himc_ime_unicode(data) ^ unicode) + if (ime_is_unicode( data->ime ) ^ unicode) { if (unicode) ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR)); @@ -1349,7 +1344,7 @@ static INT CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src string.str = comp_string; - if (is_himc_ime_unicode(data) && !unicode) + if (ime_is_unicode( data->ime ) && !unicode) { rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL); if (dst_len) @@ -1376,7 +1371,7 @@ static INT CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src rc = j; } } - else if (!is_himc_ime_unicode(data) && unicode) + else if (!ime_is_unicode( data->ime ) && unicode) { rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0); if (dst_len) @@ -1412,7 +1407,7 @@ static INT CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT { INT rc; - if (is_himc_ime_unicode(data) && !unicode) + if (ime_is_unicode( data->ime ) && !unicode) { if (tlen) { @@ -1433,7 +1428,7 @@ static INT CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT else rc = slen; } - else if (!is_himc_ime_unicode(data) && unicode) + else if (!ime_is_unicode( data->ime ) && unicode) { if (tlen) { @@ -1466,11 +1461,11 @@ static INT CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYT { int rc; - if (is_himc_ime_unicode(data) && !unicode) + if (ime_is_unicode( data->ime ) && !unicode) { rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL); } - else if (!is_himc_ime_unicode(data) && unicode) + else if (!ime_is_unicode( data->ime ) && unicode) { rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0); } @@ -1645,7 +1640,7 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID if (!(ime = ime_acquire( hkl ))) return 0; - if (!is_kbd_ime_unicode( ime )) + if (!ime_is_unicode( ime )) ret = ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); else { @@ -1681,7 +1676,7 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI if (!(ime = ime_acquire( hkl ))) return 0; - if (is_kbd_ime_unicode( ime )) + if (ime_is_unicode( ime )) ret = ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); else { @@ -1944,7 +1939,7 @@ UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA ) if (!(ime = ime_acquire( hkl ))) return 0; - if (!is_kbd_ime_unicode( ime )) + if (!ime_is_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleA ); else { @@ -1970,7 +1965,7 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) if (!(ime = ime_acquire( hkl ))) return 0; - if (is_kbd_ime_unicode( ime )) + if (ime_is_unicode( ime )) ret = ime->pImeGetRegisterWordStyle( count, styleW ); else { @@ -2204,7 +2199,7 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const if (!(ime = ime_acquire( hkl ))) return FALSE; - if (!is_kbd_ime_unicode( ime )) + if (!ime_is_unicode( ime )) ret = ime->pImeRegisterWord( readingA, style, stringA ); else { @@ -2230,7 +2225,7 @@ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const if (!(ime = ime_acquire( hkl ))) return FALSE; - if (is_kbd_ime_unicode( ime )) + if (ime_is_unicode( ime )) ret = ime->pImeRegisterWord( readingW, style, stringW ); else { @@ -2400,7 +2395,7 @@ BOOL WINAPI ImmSetCompositionStringA( dwIndex == SCS_QUERYRECONVERTSTRING)) return FALSE; - if (!is_himc_ime_unicode( data )) + if (!ime_is_unicode( data->ime )) return data->ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0); @@ -2457,7 +2452,7 @@ BOOL WINAPI ImmSetCompositionStringW( dwIndex == SCS_QUERYRECONVERTSTRING)) return FALSE; - if (is_himc_ime_unicode( data )) + if (ime_is_unicode( data->ime )) return data->ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL, @@ -2681,7 +2676,7 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons if (!(ime = ime_acquire( hkl ))) return FALSE; - if (!is_kbd_ime_unicode( ime )) + if (!ime_is_unicode( ime )) ret = ime->pImeUnregisterWord( readingA, style, stringA ); else { @@ -2707,7 +2702,7 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con if (!(ime = ime_acquire( hkl ))) return FALSE; - if (is_kbd_ime_unicode( ime )) + if (ime_is_unicode( ime )) ret = ime->pImeUnregisterWord( readingW, style, stringW ); else { @@ -2739,7 +2734,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (!is_himc_ime_unicode( data ) || (!parentA && !menuA)) + if (!ime_is_unicode( data->ime ) || (!parentA && !menuA)) ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); else { @@ -2797,7 +2792,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (is_himc_ime_unicode( data ) || (!parentW && !menuW)) + if (ime_is_unicode( data->ime ) || (!parentW && !menuW)) ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); else { @@ -2998,8 +2993,8 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD { WCHAR chr; - if (!is_himc_ime_unicode(data)) - ToAscii(data->lastVK, scancode, state, &chr, 0); + if (!ime_is_unicode( data->ime )) + ToAscii( data->lastVK, scancode, state, &chr, 0 ); else ToUnicodeEx(data->lastVK, scancode, state, &chr, 1, 0, GetKeyboardLayout(0)); uVirtKey = MAKELONG(data->lastVK,chr); From 5b694fb945eba29af8a5ec0d4c46a604c2309b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Mar 2023 17:06:24 +0100 Subject: [PATCH 112/758] win32u: Keep the current user locale when enumerating layouts. The low word of the enumerated HKL is the desired user locale, the high word is either the keyboard layout LANGID, or the keyboard "Layout Id" for additional layouts with the same LANGID. (cherry picked from commit d12dda395f498045edb44a6597cb4a482d27beaa) --- dlls/win32u/input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 73b50f6d656..15165ed93a8 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1275,10 +1275,10 @@ UINT WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts ) tmp = wcstoul( key_info->Name, NULL, 16 ); if (query_reg_ascii_value( subkey, "Layout Id", value_info, sizeof(buffer) ) && value_info->Type == REG_SZ) - tmp = MAKELONG( LOWORD( tmp ), - 0xf000 | (wcstoul( (const WCHAR *)value_info->Data, NULL, 16 ) & 0xfff) ); + tmp = 0xf000 | (wcstoul( (const WCHAR *)value_info->Data, NULL, 16 ) & 0xfff); NtClose( subkey ); + tmp = MAKELONG( LOWORD( layout ), LOWORD( tmp ) ); if (layout == UlongToHandle( tmp )) continue; count++; From eb6184e4f847ccfd7e392ef4c29ae4c7008e079c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 09:48:53 +0100 Subject: [PATCH 113/758] win32u: Keep the current user locale when loading layout. (cherry picked from commit 27c5abe3783638707928de7ad38a7a32468b6661) --- dlls/user32/input.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 2e08d0e2eda..9fb270ed3ed 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -440,7 +440,8 @@ BOOL WINAPI BlockInput(BOOL fBlockIt) HKL WINAPI LoadKeyboardLayoutW( const WCHAR *name, UINT flags ) { WCHAR layout_path[MAX_PATH], value[5]; - DWORD value_size, tmp; + LCID locale = GetUserDefaultLCID(); + DWORD id, value_size, tmp; HKEY hkey; HKL layout; @@ -450,6 +451,9 @@ HKL WINAPI LoadKeyboardLayoutW( const WCHAR *name, UINT flags ) if (HIWORD( tmp )) layout = UlongToHandle( tmp ); else layout = UlongToHandle( MAKELONG( LOWORD( tmp ), LOWORD( tmp ) ) ); + if (!((UINT_PTR)layout >> 28)) id = LOWORD( tmp ); + else id = HIWORD( layout ); /* IME or aliased layout */ + wcscpy( layout_path, L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\" ); wcscat( layout_path, name ); @@ -457,11 +461,12 @@ HKL WINAPI LoadKeyboardLayoutW( const WCHAR *name, UINT flags ) { value_size = sizeof(value); if (!RegGetValueW( hkey, NULL, L"Layout Id", RRF_RT_REG_SZ, NULL, (void *)&value, &value_size )) - layout = UlongToHandle( MAKELONG( LOWORD( tmp ), 0xf000 | (wcstoul( value, NULL, 16 ) & 0xfff) ) ); + id = 0xf000 | (wcstoul( value, NULL, 16 ) & 0xfff); RegCloseKey( hkey ); } + layout = UlongToHandle( MAKELONG( locale, id ) ); if ((flags & KLF_ACTIVATE) && NtUserActivateKeyboardLayout( layout, 0 )) return layout; /* FIXME: semi-stub: returning default layout */ From d22e8564529a9ea3a48f0f5234e126edaabe5c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Feb 2023 11:19:22 +0100 Subject: [PATCH 114/758] win32u: Prevent user locale change in NtUserActivateKeyboardLayout. (cherry picked from commit c0366cbb2e5126901de99b7742032a9ad3d9b69b) --- dlls/win32u/input.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 15165ed93a8..c7c8932e385 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1195,6 +1195,7 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) { struct user_thread_info *info = get_user_thread_info(); HKL old_layout; + LCID locale; HWND focus; TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags ); @@ -1208,6 +1209,13 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) return 0; } + if (NtQueryDefaultLocale( TRUE, &locale ) || LOWORD(layout) != locale) + { + RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED ); + FIXME_(keyboard)( "Changing user locale is not supported\n" ); + return 0; + } + if (!user_driver->pActivateKeyboardLayout( layout, flags )) return 0; From 0497d10d9d89c3add15c2819b803a9155df8a250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Feb 2023 11:19:22 +0100 Subject: [PATCH 115/758] winex11: Remove now unnecessary user locale change checks. (cherry picked from commit 9d72a71f57a0afafe49eb2bc5438c9632e1e76fe) --- dlls/winex11.drv/keyboard.c | 57 ------------------------------------- 1 file changed, 57 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index caf1dbc6b56..8a0a22c452d 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1515,39 +1515,6 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment); } -static HKL get_locale_kbd_layout(void) -{ - LCID layout; - LANGID langid; - - /* FIXME: - * - * layout = main_key_tab[kbd_layout].lcid; - * - * Winword uses return value of GetKeyboardLayout as a codepage - * to translate ANSI keyboard messages to unicode. But we have - * a problem with it: for instance Polish keyboard layout is - * identical to the US one, and therefore instead of the Polish - * locale id we return the US one. - */ - - NtQueryDefaultLocale( TRUE, &layout ); - - /* - * Microsoft Office expects this value to be something specific - * for Japanese and Korean Windows with an IME the value is 0xe001 - * We should probably check to see if an IME exists and if so then - * set this word properly. - */ - langid = PRIMARYLANGID(LANGIDFROMLCID(layout)); - if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN) - layout = MAKELONG( layout, 0xe001 ); /* IME */ - else - layout |= layout << 16; - - return (HKL)(UINT_PTR)layout; -} - /********************************************************************** * X11DRV_InitKeyboard @@ -1819,18 +1786,6 @@ void X11DRV_InitKeyboard( Display *display ) pthread_mutex_unlock( &kbd_mutex ); } -static BOOL match_x11_keyboard_layout(HKL hkl) -{ - const DWORD isIME = 0xE0000000; - HKL xHkl = get_locale_kbd_layout(); - - /* if the layout is an IME, only match the low word (LCID) */ - if (((ULONG_PTR)hkl & isIME) == isIME) - return (LOWORD(hkl) == LOWORD(xHkl)); - else - return (hkl == xHkl); -} - /*********************************************************************** * ActivateKeyboardLayout (X11DRV.@) @@ -1846,13 +1801,6 @@ BOOL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags) return FALSE; } - if (!match_x11_keyboard_layout(hkl)) - { - RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED ); - FIXME("setting keyboard of different locales not supported\n"); - return FALSE; - } - return TRUE; } @@ -1980,8 +1928,6 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl ) Display *display = thread_init_display(); TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl); - if (!match_x11_keyboard_layout(hkl)) - FIXME("keyboard layout %p is not supported\n", hkl); pthread_mutex_lock( &kbd_mutex ); @@ -2350,9 +2296,6 @@ INT X11DRV_ToUnicodeEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState, return 0; } - if (!match_x11_keyboard_layout(hkl)) - FIXME_(key)("keyboard layout %p is not supported\n", hkl); - if ((lpKeyState[VK_MENU] & 0x80) && (lpKeyState[VK_CONTROL] & 0x80)) { TRACE_(key)("Ctrl+Alt+[key] won't generate a character\n"); From 0047dd8773de1663080613e5ffcfa6ace5848663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Feb 2023 10:04:55 +0100 Subject: [PATCH 116/758] imm32: Rename szImeRegFmt to layouts_formatW. (cherry picked from commit 4eba0765d9f52e26c8cf499cac81f784ba83ddd5) --- dlls/imm32/imm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 14b357d9533..94bc0dea024 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -123,7 +123,7 @@ static CRITICAL_SECTION_DEBUG ime_cs_debug = static CRITICAL_SECTION ime_cs = { &ime_cs_debug, -1, 0, 0, 0, 0 }; static struct list ime_list = LIST_INIT( ime_list ); -static const WCHAR szImeRegFmt[] = L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lx"; +static const WCHAR layouts_formatW[] = L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lx"; static BOOL ime_is_unicode( const struct ime *ime ) { @@ -1842,9 +1842,9 @@ UINT WINAPI ImmGetIMEFileNameW(HKL hKL, LPWSTR lpszFileName, UINT uBufLen) HKEY hkey; DWORD length; DWORD rc; - WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8]; + WCHAR regKey[ARRAY_SIZE(layouts_formatW)+8]; - wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hKL ); + wsprintfW( regKey, layouts_formatW, (ULONG_PTR)hKL ); rc = RegOpenKeyW( HKEY_LOCAL_MACHINE, regKey, &hkey); if (rc != ERROR_SUCCESS) { @@ -2056,7 +2056,7 @@ HKL WINAPI ImmInstallIMEW( HKL hkl; DWORD rc; HKEY hkey; - WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8]; + WCHAR regKey[ARRAY_SIZE(layouts_formatW)+8]; TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName), debugstr_w(lpszLayoutText)); @@ -2069,7 +2069,7 @@ HKL WINAPI ImmInstallIMEW( DWORD disposition = 0; hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count ); - wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hkl); + wsprintfW( regKey, layouts_formatW, (ULONG_PTR)hkl); rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, regKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition); if (rc == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY) From 1538eb179da63af0c95b113f8cc56b88f7cba785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 20 Feb 2023 21:53:19 +0100 Subject: [PATCH 117/758] imm32: Transform "Ime File" value in ImmInstallIMEW. (cherry picked from commit 8b0b53379f99d6878e2189edb317780faf3abd55) --- dlls/imm32/imm.c | 61 +++++++++++++++++++--------------------- dlls/imm32/tests/imm32.c | 3 -- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 94bc0dea024..a915bb25132 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2048,34 +2048,30 @@ HKL WINAPI ImmInstallIMEA( /*********************************************************************** * ImmInstallIMEW (IMM32.@) */ -HKL WINAPI ImmInstallIMEW( - LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText) +HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description ) { - INT lcid = GetUserDefaultLCID(); - INT count; - HKL hkl; - DWORD rc; + WCHAR path[ARRAY_SIZE(layouts_formatW)+8], buffer[MAX_PATH]; + LCID lcid = GetUserDefaultLCID(); + WORD count = 0x20; + const WCHAR *tmp; + DWORD length; HKEY hkey; - WCHAR regKey[ARRAY_SIZE(layouts_formatW)+8]; - - TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName), - debugstr_w(lpszLayoutText)); + HKL hkl; - /* Start with 2. e001 will be blank and so default to the wine internal IME */ - count = 2; + TRACE( "filename %s, description %s\n", debugstr_w(filename), debugstr_w(description) ); while (count < 0xfff) { DWORD disposition = 0; hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count ); - wsprintfW( regKey, layouts_formatW, (ULONG_PTR)hkl); - - rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, regKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition); - if (rc == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY) - break; - else if (rc == ERROR_SUCCESS) - RegCloseKey(hkey); + swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG_PTR)hkl); + if (!RegCreateKeyExW( HKEY_LOCAL_MACHINE, path, 0, NULL, 0, + KEY_WRITE, NULL, &hkey, &disposition )) + { + if (disposition == REG_CREATED_NEW_KEY) break; + RegCloseKey( hkey ); + } count++; } @@ -2086,21 +2082,22 @@ HKL WINAPI ImmInstallIMEW( return 0; } - if (rc == ERROR_SUCCESS) - { - rc = RegSetValueExW(hkey, L"Ime File", 0, REG_SZ, (const BYTE*)lpszIMEFileName, - (lstrlenW(lpszIMEFileName) + 1) * sizeof(WCHAR)); - if (rc == ERROR_SUCCESS) - rc = RegSetValueExW(hkey, L"Layout Text", 0, REG_SZ, (const BYTE*)lpszLayoutText, - (lstrlenW(lpszLayoutText) + 1) * sizeof(WCHAR)); - RegCloseKey(hkey); - return hkl; - } - else + if ((tmp = wcsrchr( filename, '\\' ))) tmp++; + else tmp = filename; + + length = LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, tmp, -1, buffer, ARRAY_SIZE(buffer) ); + + if (RegSetValueExW( hkey, L"Ime File", 0, REG_SZ, (const BYTE *)buffer, length * sizeof(WCHAR) ) || + RegSetValueExW( hkey, L"Layout Text", 0, REG_SZ, (const BYTE *)description, + (wcslen(description) + 1) * sizeof(WCHAR) )) { - WARN("Unable to set IME registry values\n"); - return 0; + WARN( "Unable to write registry to install IME\n"); + hkl = 0; } + RegCloseKey( hkey ); + + if (!hkl) RegDeleteKeyW( HKEY_LOCAL_MACHINE, path ); + return hkl; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 16e570c4c8d..1f96431682a 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2685,7 +2685,6 @@ static HKL ime_install(void) memset( buffer, 0xcd, sizeof(buffer) ); ret = RegQueryValueExW( hkey, L"Ime File", NULL, NULL, (BYTE *)buffer, &len ); ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() ); - todo_wine ok( !wcsicmp( buffer, wcsrchr( ime_path, '\\' ) + 1 ), "got Ime File %s\n", debugstr_w(buffer) ); len = sizeof(buffer); @@ -2988,13 +2987,11 @@ static void test_ImmGetIMEFileName(void) ret = ImmGetIMEFileNameW( hkl, bufferW, 13 ); todo_wine ok( ret == 12, "ImmGetIMEFileNameW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetIMEFileNameA( hkl, bufferA, 13 ); todo_wine ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); ime_cleanup( hkl ); From eb5c3fa2a77d8cdbd3db304d5fee03dd3434b585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Feb 2023 11:00:59 +0100 Subject: [PATCH 118/758] imm32: Rewrite ImmGetIMEFileName(A|W). (cherry picked from commit 85489a53f5e839fd2e5d804a1172da25214351e5) --- dlls/imm32/imm.c | 87 +++++++++++++--------------------------- dlls/imm32/tests/imm32.c | 13 ------ 2 files changed, 27 insertions(+), 73 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index a915bb25132..6acdd8a7344 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1802,82 +1802,49 @@ DWORD WINAPI ImmGetGuideLineW(HIMC hIMC, DWORD dwIndex, LPWSTR lpBuf, DWORD dwBu } /*********************************************************************** - * ImmGetIMEFileNameA (IMM32.@) + * ImmGetIMEFileNameA (IMM32.@) */ -UINT WINAPI ImmGetIMEFileNameA( HKL hKL, LPSTR lpszFileName, UINT uBufLen) +UINT WINAPI ImmGetIMEFileNameA( HKL hkl, char *bufferA, UINT lengthA ) { - LPWSTR bufW = NULL; - UINT wBufLen = uBufLen; - UINT rc; + WCHAR *bufferW; + DWORD lengthW; - if (uBufLen && lpszFileName) - bufW = HeapAlloc(GetProcessHeap(),0,uBufLen * sizeof(WCHAR)); - else /* We need this to get the number of byte required */ - { - bufW = HeapAlloc(GetProcessHeap(),0,MAX_PATH * sizeof(WCHAR)); - wBufLen = MAX_PATH; - } - - rc = ImmGetIMEFileNameW(hKL,bufW,wBufLen); + TRACE( "hkl %p, bufferA %p, lengthA %d\n", hkl, bufferA, lengthA ); - if (rc > 0) - { - if (uBufLen && lpszFileName) - rc = WideCharToMultiByte(CP_ACP, 0, bufW, -1, lpszFileName, - uBufLen, NULL, NULL); - else /* get the length */ - rc = WideCharToMultiByte(CP_ACP, 0, bufW, -1, NULL, 0, NULL, - NULL); - } + if (!(lengthW = ImmGetIMEFileNameW( hkl, NULL, 0 ))) return 0; + if (!(bufferW = malloc( (lengthW + 1) * sizeof(WCHAR) ))) return 0; + lengthW = ImmGetIMEFileNameW( hkl, bufferW, lengthW + 1 ); + lengthA = WideCharToMultiByte( CP_ACP, 0, bufferW, lengthW, bufferA, + bufferA ? lengthA : 0, NULL, NULL ); + if (bufferA) bufferA[lengthA] = 0; + free( bufferW ); - HeapFree(GetProcessHeap(),0,bufW); - return rc; + return lengthA; } /*********************************************************************** * ImmGetIMEFileNameW (IMM32.@) */ -UINT WINAPI ImmGetIMEFileNameW(HKL hKL, LPWSTR lpszFileName, UINT uBufLen) +UINT WINAPI ImmGetIMEFileNameW( HKL hkl, WCHAR *buffer, UINT length ) { - HKEY hkey; - DWORD length; - DWORD rc; - WCHAR regKey[ARRAY_SIZE(layouts_formatW)+8]; - - wsprintfW( regKey, layouts_formatW, (ULONG_PTR)hKL ); - rc = RegOpenKeyW( HKEY_LOCAL_MACHINE, regKey, &hkey); - if (rc != ERROR_SUCCESS) - { - SetLastError(rc); - return 0; - } + WCHAR path[MAX_PATH]; + HKEY hkey = 0; + DWORD size; - length = 0; - rc = RegGetValueW(hkey, NULL, L"Ime File", RRF_RT_REG_SZ, NULL, NULL, &length); + TRACE( "hkl %p, buffer %p, length %u\n", hkl, buffer, length ); - if (rc != ERROR_SUCCESS) - { - RegCloseKey(hkey); - SetLastError(rc); - return 0; - } - if (length > uBufLen * sizeof(WCHAR) || !lpszFileName) - { - RegCloseKey(hkey); - if (lpszFileName) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return 0; - } - else - return length / sizeof(WCHAR); - } + swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG)(ULONG_PTR)hkl ); + if (RegOpenKeyW( HKEY_LOCAL_MACHINE, path, &hkey )) return 0; - RegGetValueW(hkey, NULL, L"Ime File", RRF_RT_REG_SZ, NULL, lpszFileName, &length); + size = ARRAY_SIZE(path) * sizeof(WCHAR); + if (RegGetValueW( hkey, NULL, L"Ime File", RRF_RT_REG_SZ, NULL, path, &size )) *path = 0; + RegCloseKey( hkey ); - RegCloseKey(hkey); + size = wcslen( path ); + if (!buffer) return size; - return length / sizeof(WCHAR); + lstrcpynW( buffer, path, length ); + return wcslen( buffer ); } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 1f96431682a..c1038a3adb9 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2937,60 +2937,47 @@ static void test_ImmGetIMEFileName(void) ret = ImmGetIMEFileNameA( hkl, bufferA, 100 ); ok( !ret, "ImmGetIMEFileNameA returned %lu\n", ret ); ret = GetLastError(); - todo_wine ok( ret == 0xdeadbeef, "got error %lu\n", ret ); if (!(hkl = ime_install())) goto cleanup; memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetIMEFileNameW( hkl, bufferW, 2 ); - todo_wine ok( ret == 1, "ImmGetIMEFileNameW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, L"W" ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetIMEFileNameA( hkl, bufferA, 2 ); ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.I", ime_count - 1 ); memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetIMEFileNameW( hkl, bufferW, 11 ); - todo_wine ok( ret == 10, "ImmGetIMEFileNameW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetIMEFileNameA( hkl, bufferA, 11 ); ok( ret == 0, "ImmGetIMEFileNameA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IM", ime_count - 1 ); memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetIMEFileNameW( hkl, bufferW, 12 ); - todo_wine ok( ret == 11, "ImmGetIMEFileNameW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); snprintf( expectA, ARRAY_SIZE(expectA), "WINE%04X.IME", ime_count - 1 ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetIMEFileNameA( hkl, bufferA, 12 ); - todo_wine ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); swprintf( expectW, ARRAY_SIZE(expectW), L"WINE%04X.IME", ime_count - 1 ); memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetIMEFileNameW( hkl, bufferW, 13 ); - todo_wine ok( ret == 12, "ImmGetIMEFileNameW returned %lu\n", ret ); ok( !wcscmp( bufferW, expectW ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetIMEFileNameA( hkl, bufferA, 13 ); - todo_wine ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); From 4cd14c52923930e2622e8a722d9ba2521ade6b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Feb 2023 10:43:36 +0100 Subject: [PATCH 119/758] imm32: Rewrite ImmGetDescription(A|W). (cherry picked from commit e242e094324bf520ed0cc34b78c8696decca3294) --- dlls/imm32/imm.c | 65 +++++++++++++++++++--------------------- dlls/imm32/tests/imm32.c | 15 ---------- 2 files changed, 31 insertions(+), 49 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 6acdd8a7344..6fe6b5e3f0e 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1728,52 +1728,49 @@ HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd) } /*********************************************************************** - * ImmGetDescriptionA (IMM32.@) + * ImmGetDescriptionA (IMM32.@) */ -UINT WINAPI ImmGetDescriptionA( - HKL hKL, LPSTR lpszDescription, UINT uBufLen) +UINT WINAPI ImmGetDescriptionA( HKL hkl, LPSTR bufferA, UINT lengthA ) { - WCHAR *buf; - DWORD len; - - TRACE("%p %p %d\n", hKL, lpszDescription, uBufLen); - - /* find out how many characters in the unicode buffer */ - len = ImmGetDescriptionW( hKL, NULL, 0 ); - if (!len) - return 0; - - /* allocate a buffer of that size */ - buf = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof (WCHAR) ); - if( !buf ) - return 0; - - /* fetch the unicode buffer */ - len = ImmGetDescriptionW( hKL, buf, len + 1 ); - - /* convert it back to ANSI */ - len = WideCharToMultiByte( CP_ACP, 0, buf, len + 1, - lpszDescription, uBufLen, NULL, NULL ); + WCHAR *bufferW; + DWORD lengthW; - HeapFree( GetProcessHeap(), 0, buf ); + TRACE( "hkl %p, bufferA %p, lengthA %d\n", hkl, bufferA, lengthA ); - if (len == 0) - return 0; + if (!(lengthW = ImmGetDescriptionW( hkl, NULL, 0 ))) return 0; + if (!(bufferW = malloc( (lengthW + 1) * sizeof(WCHAR) ))) return 0; + lengthW = ImmGetDescriptionW( hkl, bufferW, lengthW + 1 ); + lengthA = WideCharToMultiByte( CP_ACP, 0, bufferW, lengthW, bufferA, + bufferA ? lengthA : 0, NULL, NULL ); + if (bufferA) bufferA[lengthA] = 0; + free( bufferW ); - return len - 1; + return lengthA; } /*********************************************************************** * ImmGetDescriptionW (IMM32.@) */ -UINT WINAPI ImmGetDescriptionW(HKL hKL, LPWSTR lpszDescription, UINT uBufLen) +UINT WINAPI ImmGetDescriptionW( HKL hkl, WCHAR *buffer, UINT length ) { - FIXME("(%p, %p, %d): semi stub\n", hKL, lpszDescription, uBufLen); + WCHAR path[MAX_PATH]; + HKEY hkey = 0; + DWORD size; + + TRACE( "hkl %p, buffer %p, length %u\n", hkl, buffer, length ); + + swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG)(ULONG_PTR)hkl ); + if (RegOpenKeyW( HKEY_LOCAL_MACHINE, path, &hkey )) return 0; - if (!hKL) return 0; - if (!uBufLen) return lstrlenW(L"Wine XIM" ); - lstrcpynW( lpszDescription, L"Wine XIM", uBufLen ); - return lstrlenW( lpszDescription ); + size = ARRAY_SIZE(path) * sizeof(WCHAR); + if (RegGetValueW( hkey, NULL, L"Layout Text", RRF_RT_REG_SZ, NULL, path, &size )) *path = 0; + RegCloseKey( hkey ); + + size = wcslen( path ); + if (!buffer) return size; + + lstrcpynW( buffer, path, length ); + return wcslen( buffer ); } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index c1038a3adb9..1ab12a6d84c 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2843,10 +2843,8 @@ static void test_ImmGetDescription(void) ret = ImmGetDescriptionA( NULL, NULL, 100 ); ok( !ret, "ImmGetDescriptionA returned %lu\n", ret ); ret = ImmGetDescriptionW( hkl, bufferW, 100 ); - todo_wine ok( !ret, "ImmGetDescriptionW returned %lu\n", ret ); ret = ImmGetDescriptionA( hkl, bufferA, 100 ); - todo_wine ok( !ret, "ImmGetDescriptionA returned %lu\n", ret ); ret = GetLastError(); ok( ret == 0xdeadbeef, "got error %lu\n", ret ); @@ -2860,46 +2858,33 @@ static void test_ImmGetDescription(void) memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetDescriptionA( hkl, bufferA, 2 ); ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetDescriptionW( hkl, bufferW, 11 ); - todo_wine ok( ret == 10, "ImmGetDescriptionW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, L"WineTest I" ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetDescriptionA( hkl, bufferA, 11 ); - todo_wine ok( ret == 0, "ImmGetDescriptionA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, "" ), "got bufferA %s\n", debugstr_a(bufferA) ); memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetDescriptionW( hkl, bufferW, 12 ); - todo_wine ok( ret == 11, "ImmGetDescriptionW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, L"WineTest IM" ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetDescriptionA( hkl, bufferA, 12 ); - todo_wine ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetDescriptionW( hkl, bufferW, 13 ); - todo_wine ok( ret == 12, "ImmGetDescriptionW returned %lu\n", ret ); - todo_wine ok( !wcscmp( bufferW, L"WineTest IME" ), "got bufferW %s\n", debugstr_w(bufferW) ); memset( bufferA, 0xcd, sizeof(bufferA) ); ret = ImmGetDescriptionA( hkl, bufferA, 13 ); - todo_wine ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret ); - todo_wine ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); ime_cleanup( hkl ); From bb190292576632314a1a763755a35648fec5623a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 20 Feb 2023 21:58:37 +0100 Subject: [PATCH 120/758] imm32: Use CRT allocation functions. (cherry picked from commit 8bdee1d248b8f549c81ebea56e17505214d1e600) --- dlls/imm32/imm.c | 112 +++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 6fe6b5e3f0e..682fb748f2f 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -139,8 +139,7 @@ static inline WCHAR *strdupAtoW( const char *str ) if (str) { DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) - MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); + if ((ret = malloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); } return ret; } @@ -151,8 +150,7 @@ static inline CHAR *strdupWtoA( const WCHAR *str ) if (str) { DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); - if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) - WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); + if ((ret = malloc( len ))) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); } return ret; } @@ -300,7 +298,7 @@ static ULONG WINAPI InitializeSpy_Release(IInitializeSpy *iface) LONG ref = InterlockedDecrement(&spy->ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, spy); + free( spy ); NtUserGetThreadInfo()->client_imm = 0; } return ref; @@ -379,7 +377,7 @@ static void imm_coinit_thread(void) if (!(spy = get_thread_coinit_spy())) { - if (!(spy = HeapAlloc(GetProcessHeap(), 0, sizeof(*spy)))) return; + if (!(spy = malloc( sizeof(*spy) ))) return; spy->IInitializeSpy_iface.lpVtbl = &InitializeSpyVtbl; spy->ref = 1; spy->cookie.QuadPart = 0; @@ -596,7 +594,7 @@ static BOOL free_input_context_data( HIMC hIMC ) ImmDestroyIMCC( data->IMC.hMsgBuf ); ime_release( data->ime ); - HeapFree( GetProcessHeap(), 0, data ); + free( data ); return TRUE; } @@ -822,8 +820,8 @@ BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data ) wordW.lpWord = strdupAtoW( wordA->lpWord ); wordW.lpReading = strdupAtoW( wordA->lpReading ); ret = ime->pImeConfigure( hkl, hwnd, mode, &wordW ); - HeapFree( GetProcessHeap(), 0, wordW.lpReading ); - HeapFree( GetProcessHeap(), 0, wordW.lpWord ); + free( wordW.lpReading ); + free( wordW.lpWord ); } ime_release( ime ); @@ -852,8 +850,8 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) wordA.lpWord = strdupWtoA( wordW->lpWord ); wordA.lpReading = strdupWtoA( wordW->lpReading ); ret = ime->pImeConfigure( hkl, hwnd, mode, &wordA ); - HeapFree( GetProcessHeap(), 0, wordA.lpReading ); - HeapFree( GetProcessHeap(), 0, wordA.lpWord ); + free( wordA.lpReading ); + free( wordA.lpWord ); } ime_release( ime ); @@ -867,14 +865,14 @@ static InputContextData *create_input_context(HIMC default_imc) LPCANDIDATEINFO ci; int i; - new_context = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(InputContextData)); + new_context = calloc( 1, sizeof(InputContextData) ); /* Load the IME */ new_context->threadDefault = !!default_imc; if (!(new_context->ime = ime_acquire( GetKeyboardLayout( 0 ) ))) { TRACE("IME dll could not be loaded\n"); - HeapFree(GetProcessHeap(),0,new_context); + free( new_context ); return 0; } @@ -981,8 +979,8 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch { WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); ret = ime->pImeEnumRegisterWord( procA, readingW, style, stringW, user ); - HeapFree( GetProcessHeap(), 0, readingW ); - HeapFree( GetProcessHeap(), 0, stringW ); + free( readingW ); + free( stringW ); } ime_release( ime ); @@ -1009,8 +1007,8 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC { char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); ret = ime->pImeEnumRegisterWord( procW, readingA, style, stringA, user ); - HeapFree( GetProcessHeap(), 0, readingA ); - HeapFree( GetProcessHeap(), 0, stringA ); + free( readingA ); + free( stringA ); } ime_release( ime ); @@ -1648,14 +1646,14 @@ DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDID WCHAR *srcW = strdupAtoW( srcA ); DWORD lengthW = ime->pImeConversionList( himc, srcW, NULL, 0, flags ); - if (!(listW = HeapAlloc( GetProcessHeap(), 0, lengthW ))) ret = 0; + if (!(listW = malloc( lengthW ))) ret = 0; else { ime->pImeConversionList( himc, srcW, listW, lengthW, flags ); ret = convert_candidatelist_WtoA( listW, listA, lengthA ); - HeapFree( GetProcessHeap(), 0, listW ); + free( listW ); } - HeapFree( GetProcessHeap(), 0, srcW ); + free( srcW ); } ime_release( ime ); @@ -1684,14 +1682,14 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI char *srcA = strdupWtoA( srcW ); DWORD lengthA = ime->pImeConversionList( himc, srcA, NULL, 0, flags ); - if (!(listA = HeapAlloc( GetProcessHeap(), 0, lengthA ))) ret = 0; + if (!(listA = malloc( lengthA ))) ret = 0; else { ime->pImeConversionList( himc, srcA, listA, lengthA, flags ); ret = convert_candidatelist_AtoW( listA, listW, lengthW ); - HeapFree( GetProcessHeap(), 0, listA ); + free( listA ); } - HeapFree( GetProcessHeap(), 0, srcA ); + free( srcA ); } ime_release( ime ); @@ -1989,23 +1987,17 @@ UINT WINAPI ImmGetVirtualKey(HWND hWnd) /*********************************************************************** * ImmInstallIMEA (IMM32.@) */ -HKL WINAPI ImmInstallIMEA( - LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText) +HKL WINAPI ImmInstallIMEA( const char *filenameA, const char *descriptionA ) { - LPWSTR lpszwIMEFileName; - LPWSTR lpszwLayoutText; + WCHAR *filenameW = strdupAtoW( filenameA ), *descriptionW = strdupAtoW( descriptionA ); HKL hkl; - TRACE ("(%s, %s)\n", debugstr_a(lpszIMEFileName), - debugstr_a(lpszLayoutText)); + TRACE( "filenameA %s, descriptionA %s\n", debugstr_a(filenameA), debugstr_a(descriptionA) ); - lpszwIMEFileName = strdupAtoW(lpszIMEFileName); - lpszwLayoutText = strdupAtoW(lpszLayoutText); + hkl = ImmInstallIMEW( filenameW, descriptionW ); + free( descriptionW ); + free( filenameW ); - hkl = ImmInstallIMEW(lpszwIMEFileName, lpszwLayoutText); - - HeapFree(GetProcessHeap(),0,lpszwIMEFileName); - HeapFree(GetProcessHeap(),0,lpszwLayoutText); return hkl; } @@ -2024,6 +2016,12 @@ HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description ) TRACE( "filename %s, description %s\n", debugstr_w(filename), debugstr_w(description) ); + if (!filename || !description) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + while (count < 0xfff) { DWORD disposition = 0; @@ -2166,8 +2164,8 @@ BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const { WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); ret = ime->pImeRegisterWord( readingW, style, stringW ); - HeapFree( GetProcessHeap(), 0, readingW ); - HeapFree( GetProcessHeap(), 0, stringW ); + free( readingW ); + free( stringW ); } ime_release( ime ); @@ -2192,8 +2190,8 @@ BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const { char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); ret = ime->pImeRegisterWord( readingA, style, stringA ); - HeapFree( GetProcessHeap(), 0, readingA ); - HeapFree( GetProcessHeap(), 0, stringA ); + free( readingA ); + free( stringA ); } ime_release( ime ); @@ -2362,22 +2360,22 @@ BOOL WINAPI ImmSetCompositionStringA( comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0); if (comp_len) { - CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len * sizeof(WCHAR)); + CompBuffer = malloc( comp_len * sizeof(WCHAR) ); MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len); } read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0); if (read_len) { - ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len * sizeof(WCHAR)); + ReadBuffer = malloc( read_len * sizeof(WCHAR) ); MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len); } rc = ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len, ReadBuffer, read_len); - HeapFree(GetProcessHeap(), 0, CompBuffer); - HeapFree(GetProcessHeap(), 0, ReadBuffer); + free( CompBuffer ); + free( ReadBuffer ); return rc; } @@ -2420,7 +2418,7 @@ BOOL WINAPI ImmSetCompositionStringW( NULL); if (comp_len) { - CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len); + CompBuffer = malloc( comp_len ); WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len, NULL, NULL); } @@ -2429,7 +2427,7 @@ BOOL WINAPI ImmSetCompositionStringW( NULL); if (read_len) { - ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len); + ReadBuffer = malloc( read_len ); WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len, NULL, NULL); } @@ -2437,8 +2435,8 @@ BOOL WINAPI ImmSetCompositionStringW( rc = ImmSetCompositionStringA(hIMC, dwIndex, CompBuffer, comp_len, ReadBuffer, read_len); - HeapFree(GetProcessHeap(), 0, CompBuffer); - HeapFree(GetProcessHeap(), 0, ReadBuffer); + free( CompBuffer ); + free( ReadBuffer ); return rc; } @@ -2643,8 +2641,8 @@ BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, cons { WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); ret = ime->pImeUnregisterWord( readingW, style, stringW ); - HeapFree( GetProcessHeap(), 0, readingW ); - HeapFree( GetProcessHeap(), 0, stringW ); + free( readingW ); + free( stringW ); } ime_release( ime ); @@ -2669,8 +2667,8 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con { char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); ret = ime->pImeUnregisterWord( readingA, style, stringA ); - HeapFree( GetProcessHeap(), 0, readingA ); - HeapFree( GetProcessHeap(), 0, stringA ); + free( readingA ); + free( stringA ); } ime_release( ime ); @@ -2706,7 +2704,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE { int count = size / sizeof(LPIMEMENUITEMINFOA); size = count * sizeof(IMEMENUITEMINFOW); - menuW = HeapAlloc( GetProcessHeap(), 0, size ); + menuW = malloc( size ); } ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); @@ -2729,7 +2727,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE IMEMENUITEM_STRING_SIZE, NULL, NULL ); } } - HeapFree( GetProcessHeap(), 0, menuW ); + free( menuW ); } return ret; @@ -2764,7 +2762,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE { int count = size / sizeof(LPIMEMENUITEMINFOW); size = count * sizeof(IMEMENUITEMINFOA); - menuA = HeapAlloc( GetProcessHeap(), 0, size ); + menuA = malloc( size ); } ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); @@ -2785,7 +2783,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE MultiByteToWideChar( CP_ACP, 0, menuA[i].szString, -1, menuW[i].szString, IMEMENUITEM_STRING_SIZE ); } } - HeapFree( GetProcessHeap(), 0, menuA ); + free( menuA ); } return ret; @@ -2947,7 +2945,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD GetKeyboardState(state); scancode = lKeyData >> 0x10 & 0xff; - list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list_count * sizeof(TRANSMSG) + sizeof(DWORD)); + list = calloc( list_count, sizeof(TRANSMSG) + sizeof(DWORD) ); list->uMsgCount = list_count; if (data->ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) @@ -2976,7 +2974,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD else if (msg_count > list_count) ImmGenerateMessage(imc); - HeapFree(GetProcessHeap(),0,list); + free( list ); data->lastVK = VK_PROCESSKEY; From 56351955ebda75351efbe0cd4f764275a56d3d04 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 27 Feb 2023 16:10:15 +0800 Subject: [PATCH 121/758] user32/tests: Do not modify cursor position when simulating clicks. FVWM by default uses a focus follow mouse model so the window under the mouse cursor automatically gets focus. Windows explorer.exe and other windows managers use click to focus model. So on FVWM, if a test changes the cursor position, it might affects other tests that rely on a specific focus window. Restore the cursor position after sending simulating clicks to avoid affecting other tests unintentionally. FVWM could be configured to use a click to focus model, but right now it will make many tests starting to succeed and other tests fail. So it seems to be too big of a change. Flaky is added to test_SetWindowPos() because this patch makes the tests succeed on Gitlab CI but the same tests still fails for other window managers and on TestBots. (cherry picked from commit 40492eb0071478812436f3e21a2cd4aee1b7bccd) --- dlls/user32/tests/win.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 1252e046ad3..31d14480861 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -3282,7 +3282,7 @@ static void test_SetWindowPos(HWND hwnd, HWND hwnd2) ret = SetWindowPos(hwnd_child, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_SHOWWINDOW); ok(ret, "Got %d\n", ret); flush_events( TRUE ); - todo_wine check_active_state(hwnd2, hwnd2, hwnd2); + flaky todo_wine check_active_state(hwnd2, hwnd2, hwnd2); DestroyWindow(hwnd_child); } @@ -10186,7 +10186,9 @@ static void simulate_click(int x, int y) { INPUT input[2]; UINT events_no; + POINT pt; + GetCursorPos(&pt); SetCursorPos(x, y); memset(input, 0, sizeof(input)); input[0].type = INPUT_MOUSE; @@ -10199,6 +10201,7 @@ static void simulate_click(int x, int y) U(input[1]).mi.dwFlags = MOUSEEVENTF_LEFTUP; events_no = SendInput(2, input, sizeof(input[0])); ok(events_no == 2, "SendInput returned %d\n", events_no); + SetCursorPos(pt.x, pt.y); } static WNDPROC def_static_proc; From bb7fa3f5955c3f760a7ea9d66d707d1adbe201ce Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Tue, 28 Feb 2023 19:22:54 +0200 Subject: [PATCH 122/758] win32u: Allow drivers to set the null user driver. Allow passing NULL as the user driver to __wine_set_user_driver(), to set the internal null user driver. This is useful for drivers that may need to tentatively set their own user driver during setup and reset it on failure. Signed-off-by: Alexandros Frantzis (cherry picked from commit 243c19098ee2e5c87a6bdbebdbb4c5cf598a9de2) --- dlls/win32u/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index eb736de272d..5bae77da5f4 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -1241,7 +1241,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version } driver = malloc( sizeof(*driver) ); - *driver = *funcs; + *driver = funcs ? *funcs : null_user_driver; #define SET_USER_FUNC(name) \ do { if (!driver->p##name) driver->p##name = nulldrv_##name; } while(0) From b035fb91aeee2a318f5aacb1e9cb6898cb46d310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 16 Mar 2023 11:25:34 +0100 Subject: [PATCH 123/758] imm32/tests: Use LANG_INVARIANT for the installed IME. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54669 (cherry picked from commit 7f5007f19737449a147d7c4ae356a515df264854) --- dlls/imm32/tests/ime_wrapper.rc | 2 +- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/tests/ime_wrapper.rc b/dlls/imm32/tests/ime_wrapper.rc index 6844b62195b..812754ce4ce 100644 --- a/dlls/imm32/tests/ime_wrapper.rc +++ b/dlls/imm32/tests/ime_wrapper.rc @@ -18,7 +18,7 @@ #pragma makedep testdll -#define WINE_LANGID 0400 +#define WINE_LANGID 047f #define WINE_FILETYPE VFT_DRV #define WINE_FILESUBTYPE VFT2_DRV_INPUTMETHOD #define WINE_FILENAME "ime_wrapper" diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 1ab12a6d84c..646c3a2127d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2675,7 +2675,7 @@ static HKL ime_install(void) hkl = ImmInstallIMEW( ime_path, L"WineTest IME" ); todo_wine - ok( hkl == (HKL)(int)0xe0200400, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() ); + ok( hkl == (HKL)(int)0xe020047f, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() ); swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl ); ret = RegOpenKeyW( HKEY_LOCAL_MACHINE, buffer, &hkey ); From 9d6a7c7ed42d988e7749f7d3703a3a3619ac9718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 22:46:00 +0100 Subject: [PATCH 124/758] imm32/tests: Test ImmIsIME with the installed IME. (cherry picked from commit bb8bcf492c80dcd24cd0469d6da01a4837eeaa29) --- dlls/imm32/tests/imm32.c | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 646c3a2127d..72ebde6a2c7 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -35,6 +35,28 @@ #include "ime_test.h" +static const char *debugstr_ok( const char *cond ) +{ + int c, n = 0; + /* skip possible casts */ + while ((c = *cond++)) + { + if (c == '(') n++; + if (!n) break; + if (c == ')') n--; + } + if (!strchr( cond - 1, '(' )) return wine_dbg_sprintf( "got %s", cond - 1 ); + return wine_dbg_sprintf( "%.*s returned", (int)strcspn( cond - 1, "( " ), cond - 1 ); +} + +#define ok_eq( e, r, t, f, ... ) \ + do \ + { \ + t v = (r); \ + ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ + } while (0) +#define ok_ret( e, r ) ok_eq( e, r, UINT_PTR, "%Iu, error %ld", GetLastError() ) + BOOL WINAPI ImmSetActiveContext(HWND, HIMC, BOOL); static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD); @@ -2425,9 +2447,13 @@ static void test_ImmDisableIME(void) #define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) +static BOOL todo_ImeInquire; DEFINE_EXPECT( ImeInquire ); +static BOOL todo_ImeDestroy; DEFINE_EXPECT( ImeDestroy ); +static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); +static BOOL todo_IME_DLL_PROCESS_DETACH; DEFINE_EXPECT( IME_DLL_PROCESS_DETACH ); static IMEINFO ime_info; @@ -2468,6 +2494,7 @@ static BOOL WINAPI ime_ImeDestroy( UINT force ) { ime_trace( "force %u\n", force ); + todo_wine_if( todo_ImeDestroy ) CHECK_EXPECT( ImeDestroy ); ok( !force, "got force %u\n", force ); @@ -2511,6 +2538,7 @@ static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) { ime_trace( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags ); + todo_wine_if( todo_ImeInquire ) CHECK_EXPECT( ImeInquire ); ok( !!info, "got info %p\n", info ); @@ -2598,11 +2626,13 @@ static BOOL WINAPI ime_DllMain( HINSTANCE instance, DWORD reason, LPVOID reserve DisableThreadLibraryCalls( instance ); ime_ui_class.hInstance = instance; RegisterClassExW( &ime_ui_class ); + todo_wine_if(todo_IME_DLL_PROCESS_ATTACH) CHECK_EXPECT( IME_DLL_PROCESS_ATTACH ); break; case DLL_PROCESS_DETACH: UnregisterClassW( ime_ui_class.lpszClassName, instance ); + todo_wine_if(todo_IME_DLL_PROCESS_DETACH) CHECK_EXPECT( IME_DLL_PROCESS_DETACH ); break; } @@ -2821,6 +2851,45 @@ static void test_ImmInstallIME(void) SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); } +static void test_ImmIsIME(void) +{ + HKL hkl = GetKeyboardLayout( 0 ); + + SET_ENABLE( IME_DLL_PROCESS_ATTACH, TRUE ); + SET_ENABLE( ImeInquire, TRUE ); + SET_ENABLE( ImeDestroy, TRUE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE ); + + SetLastError( 0xdeadbeef ); + todo_wine + ok_ret( 0, ImmIsIME( 0 ) ); + ok_ret( 0xdeadbeef, GetLastError() ); + ok_ret( 1, ImmIsIME( hkl ) ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + + if (!(hkl = ime_install())) goto cleanup; + + todo_ImeInquire = TRUE; + todo_ImeDestroy = TRUE; + todo_IME_DLL_PROCESS_ATTACH = TRUE; + todo_IME_DLL_PROCESS_DETACH = TRUE; + ok_ret( 1, ImmIsIME( hkl ) ); + todo_IME_DLL_PROCESS_ATTACH = FALSE; + todo_IME_DLL_PROCESS_DETACH = FALSE; + todo_ImeInquire = FALSE; + todo_ImeDestroy = FALSE; + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); + SET_ENABLE( ImeInquire, FALSE ); + SET_ENABLE( ImeDestroy, FALSE ); + SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); +} + static void test_ImmGetDescription(void) { HKL hkl = GetKeyboardLayout( 0 ); @@ -2988,6 +3057,7 @@ START_TEST(imm32) test_ImmInstallIME(); test_ImmGetDescription(); test_ImmGetIMEFileName(); + test_ImmIsIME(); if (init()) { From ed4b1c659de7b956419a27c50e998cee8a315629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 22:46:00 +0100 Subject: [PATCH 125/758] imm32/tests: Test ImmGetProperty with the installed IME. (cherry picked from commit 08a6d7687aaaedcde3fb7c3c1c32910ea651a627) --- dlls/imm32/tests/imm32.c | 93 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 72ebde6a2c7..fb6fe1cb726 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2890,6 +2890,98 @@ static void test_ImmIsIME(void) SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); } +static void test_ImmGetProperty(void) +{ + static const IMEINFO expect_ime_info = + { + .fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET, + }; + static const IMEINFO expect_ime_info_0411 = /* MS Japanese IME */ + { + .fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa, + .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA, + .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE | IME_SMODE_CONVERSATION, + .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD, + .fdwSelectCaps = SELECT_CAP_CONVERSION | SELECT_CAP_SENTENCE, + .fdwUICaps = UI_CAP_ROT90, + }; + static const IMEINFO expect_ime_info_0412 = /* MS Korean IME */ + { + .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa, + .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE, + .fdwSentenceCaps = IME_SMODE_NONE, + .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING, + .fdwSelectCaps = SELECT_CAP_CONVERSION, + .fdwUICaps = UI_CAP_ROT90, + }; + static const IMEINFO expect_ime_info_0804 = /* MS Chinese IME */ + { + .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa, + .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE, + .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE, + .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD, + .fdwUICaps = UI_CAP_ROT90, + }; + HKL hkl = GetKeyboardLayout( 0 ); + const IMEINFO *expect; + + SET_ENABLE( ImeInquire, TRUE ); + SET_ENABLE( ImeDestroy, TRUE ); + + SetLastError( 0xdeadbeef ); + ok_ret( 0, ImmGetProperty( 0, 0 ) ); + ok_ret( 0, ImmGetProperty( hkl, 0 ) ); + + if (hkl == (HKL)0x04110411) expect = &expect_ime_info_0411; + else if (hkl == (HKL)0x04120412) expect = &expect_ime_info_0412; + else if (hkl == (HKL)0x08040804) expect = &expect_ime_info_0804; + else expect = &expect_ime_info; + + ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) ); + todo_wine + ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) ); + todo_wine + ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) ); + ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) ); + todo_wine + ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) ); + ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) ); + ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) ); + ok_ret( 0xdeadbeef, GetLastError() ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + + if (!(hkl = ime_install())) goto cleanup; + + SET_EXPECT( ImeInquire ); + SET_EXPECT( ImeDestroy ); + ok_ret( 0, ImmGetProperty( hkl, 0 ) ); + CHECK_CALLED( ImeInquire ); + CHECK_CALLED( ImeDestroy ); + + expect = &ime_info; + todo_ImeInquire = TRUE; + todo_ImeDestroy = TRUE; + ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) ); + ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) ); + ok_ret( expect->fdwSentenceCaps, ImmGetProperty( hkl, IGP_SENTENCE ) ); + ok_ret( expect->fdwSCSCaps, ImmGetProperty( hkl, IGP_SETCOMPSTR ) ); + ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) ); + ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) ); + ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) ); + todo_ImeInquire = FALSE; + called_ImeInquire = FALSE; + todo_ImeDestroy = FALSE; + called_ImeDestroy = FALSE; + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( ImeInquire, FALSE ); + SET_ENABLE( ImeDestroy, FALSE ); +} + static void test_ImmGetDescription(void) { HKL hkl = GetKeyboardLayout( 0 ); @@ -3058,6 +3150,7 @@ START_TEST(imm32) test_ImmGetDescription(); test_ImmGetIMEFileName(); test_ImmIsIME(); + test_ImmGetProperty(); if (init()) { From 0bef95f013f16cc73f06a43b75b3fa240bcc8c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 19:33:43 +0100 Subject: [PATCH 126/758] imm32/tests: Test ImmEscape with the installed IME. (cherry picked from commit c959a58801183ef407cc2d2b4a149bafa8eb014c) --- dlls/imm32/tests/imm32.c | 161 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index fb6fe1cb726..9df89a406c0 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -55,6 +55,18 @@ static const char *debugstr_ok( const char *cond ) t v = (r); \ ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ } while (0) +#define ok_wcs( e, r ) \ + do \ + { \ + const WCHAR *v = (r); \ + ok( !wcscmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_w(v) ); \ + } while (0) +#define ok_str( e, r ) \ + do \ + { \ + const char *v = (r); \ + ok( !strcmp( v, (e) ), "%s %s\n", debugstr_ok(#r), debugstr_a(v) ); \ + } while (0) #define ok_ret( e, r ) ok_eq( e, r, UINT_PTR, "%Iu, error %ld", GetLastError() ) BOOL WINAPI ImmSetActiveContext(HWND, HIMC, BOOL); @@ -2451,6 +2463,7 @@ static BOOL todo_ImeInquire; DEFINE_EXPECT( ImeInquire ); static BOOL todo_ImeDestroy; DEFINE_EXPECT( ImeDestroy ); +DEFINE_EXPECT( ImeEscape ); static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); static BOOL todo_IME_DLL_PROCESS_DETACH; @@ -2514,7 +2527,40 @@ static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WC static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data ) { ime_trace( "himc %p, escape %#x, data %p\n", himc, escape, data ); - ok( 0, "unexpected call\n" ); + + CHECK_EXPECT( ImeEscape ); + + switch (escape) + { + case IME_ESC_SET_EUDC_DICTIONARY: + if (!data) return 4; + if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + todo_wine_if(*(WCHAR *)data != 'E') + ok_wcs( L"EscapeIme", data ); + } + else + { + todo_wine_if(*(char *)data != 'E') + ok_str( "EscapeIme", data ); + } + /* fallthrough */ + case IME_ESC_QUERY_SUPPORT: + case IME_ESC_SEQUENCE_TO_INTERNAL: + case IME_ESC_GET_EUDC_DICTIONARY: + case IME_ESC_MAX_KEY: + case IME_ESC_IME_NAME: + case IME_ESC_HANJA_MODE: + case IME_ESC_GETHELPFILENAME: + if (!data) return 4; + if (ime_info.fdwProperty & IME_PROP_UNICODE) wcscpy( data, L"ImeEscape" ); + else strcpy( data, "ImeEscape" ); + return 4; + } + + ok_eq( 0xdeadbeef, escape, UINT, "%#x" ); + ok_eq( NULL, data, void *, "%p" ); + return TRUE; } @@ -3136,6 +3182,116 @@ static void test_ImmGetIMEFileName(void) SET_ENABLE( IME_DLL_PROCESS_DETACH, FALSE ); } +static void test_ImmEscape( BOOL unicode ) +{ + HKL hkl = GetKeyboardLayout( 0 ); + DWORD i, codes[] = + { + IME_ESC_QUERY_SUPPORT, + IME_ESC_SEQUENCE_TO_INTERNAL, + IME_ESC_GET_EUDC_DICTIONARY, + IME_ESC_SET_EUDC_DICTIONARY, + IME_ESC_MAX_KEY, + IME_ESC_IME_NAME, + IME_ESC_HANJA_MODE, + IME_ESC_GETHELPFILENAME, + }; + WCHAR bufferW[512]; + char bufferA[512]; + + SET_ENABLE( ImeEscape, TRUE ); + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + SetLastError( 0xdeadbeef ); + ok_ret( 0, ImmEscapeW( hkl, 0, 0, NULL ) ); + ok_ret( 0, ImmEscapeA( hkl, 0, 0, NULL ) ); + todo_wine + ok_ret( 0xdeadbeef, GetLastError() ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + for (i = 0; i < ARRAY_SIZE(codes); ++i) + { + winetest_push_context( "esc %#lx", codes[i] ); + + SET_EXPECT( ImeEscape ); + ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], NULL ) ); + CHECK_CALLED( ImeEscape ); + + SET_EXPECT( ImeEscape ); + memset( bufferW, 0xcd, sizeof(bufferW) ); + if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) wcscpy( bufferW, L"EscapeIme" ); + ok_ret( 4, ImmEscapeW( hkl, 0, codes[i], bufferW ) ); + if (unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME || + codes[i] == IME_ESC_GETHELPFILENAME) + { + ok_wcs( L"ImeEscape", bufferW ); + ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" ); + } + else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) + { + ok_wcs( L"EscapeIme", bufferW ); + ok_eq( 0xcdcd, bufferW[10], WORD, "%#x" ); + } + else if (codes[i] == IME_ESC_HANJA_MODE) + { + todo_wine + ok_eq( 0xcdcd, bufferW[0], WORD, "%#x" ); + } + else + { + ok( !memcmp( bufferW, "ImeEscape", 10 ), "got bufferW %s\n", debugstr_w(bufferW) ); + ok_eq( 0xcdcd, bufferW[5], WORD, "%#x" ); + } + CHECK_CALLED( ImeEscape ); + + SET_EXPECT( ImeEscape ); + ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], NULL ) ); + CHECK_CALLED( ImeEscape ); + + SET_EXPECT( ImeEscape ); + memset( bufferA, 0xcd, sizeof(bufferA) ); + if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) strcpy( bufferA, "EscapeIme" ); + ok_ret( 4, ImmEscapeA( hkl, 0, codes[i], bufferA ) ); + if (!unicode || codes[i] == IME_ESC_GET_EUDC_DICTIONARY || codes[i] == IME_ESC_IME_NAME || + codes[i] == IME_ESC_GETHELPFILENAME) + { + ok_str( "ImeEscape", bufferA ); + ok_eq( 0xcd, bufferA[10], BYTE, "%#x" ); + } + else if (codes[i] == IME_ESC_SET_EUDC_DICTIONARY) + { + ok_str( "EscapeIme", bufferA ); + ok_eq( 0xcd, bufferA[10], BYTE, "%#x" ); + } + else if (codes[i] == IME_ESC_HANJA_MODE) + { + todo_wine + ok_eq( 0xcd, bufferA[0], BYTE, "%#x" ); + } + else + { + ok( !memcmp( bufferA, L"ImeEscape", 10 * sizeof(WCHAR) ), "got bufferA %s\n", debugstr_a(bufferA) ); + ok_eq( 0xcd, bufferA[20], BYTE, "%#x" ); + } + CHECK_CALLED( ImeEscape ); + + winetest_pop_context(); + } + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( ImeEscape, FALSE ); + + winetest_pop_context(); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -3152,6 +3308,9 @@ START_TEST(imm32) test_ImmIsIME(); test_ImmGetProperty(); + test_ImmEscape( FALSE ); + test_ImmEscape( TRUE ); + if (init()) { test_ImmNotifyIME(); From e38d80c9565ad25801ba677bc0e445ee8c31acbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 18 Feb 2023 11:10:35 +0100 Subject: [PATCH 127/758] imm32/tests: Test ImmEnumRegisterWord with the installed IME. (cherry picked from commit f05d4cb0c7395e6f913fdfa6a964ba83f29dfcc5) --- dlls/imm32/tests/imm32.c | 98 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 9df89a406c0..4feb423f84e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2464,6 +2464,7 @@ DEFINE_EXPECT( ImeInquire ); static BOOL todo_ImeDestroy; DEFINE_EXPECT( ImeDestroy ); DEFINE_EXPECT( ImeEscape ); +DEFINE_EXPECT( ImeEnumRegisterWord ); static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); static BOOL todo_IME_DLL_PROCESS_DETACH; @@ -2520,7 +2521,28 @@ static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WC { ime_trace( "proc %p, reading %s, style %lu, string %s, data %p\n", proc, debugstr_w(reading), style, debugstr_w(string), data ); - ok( 0, "unexpected call\n" ); + + CHECK_EXPECT( ImeEnumRegisterWord ); + + if (!style) + { + ok_eq( 0, reading, const void *, "%p" ); + ok_eq( 0, string, const void *, "%p" ); + } + else if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + ok_eq( 0xdeadbeef, style, UINT, "%#x" ); + ok_wcs( L"Reading", reading ); + ok_wcs( L"String", string ); + } + else + { + ok_eq( 0xdeadbeef, style, UINT, "%#x" ); + ok_str( "Reading", (char *)reading ); + ok_str( "String", (char *)string ); + } + + if (style) return proc( reading, style, string, data ); return 0; } @@ -3292,6 +3314,78 @@ static void test_ImmEscape( BOOL unicode ) winetest_pop_context(); } +static int CALLBACK enum_register_wordA( const char *reading, DWORD style, const char *string, void *user ) +{ + ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_a(reading), style, debugstr_a(string), user ); + + ok_eq( 0xdeadbeef, style, UINT, "%#x" ); + todo_wine_if( reading[1] == 0 ) + ok_str( "Reading", reading ); + todo_wine_if( string[1] == 0 ) + ok_str( "String", string ); + + return 0xdeadbeef; +} + +static int CALLBACK enum_register_wordW( const WCHAR *reading, DWORD style, const WCHAR *string, void *user ) +{ + ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_w(reading), style, debugstr_w(string), user ); + + ok_eq( 0xdeadbeef, style, UINT, "%#x" ); + todo_wine_if( reading[0] != 'R' ) + ok_wcs( L"Reading", reading ); + todo_wine_if( string[0] != 'S' ) + ok_wcs( L"String", string ); + + return 0xdeadbeef; +} + +static void test_ImmEnumRegisterWord( BOOL unicode ) +{ + HKL hkl = GetKeyboardLayout( 0 ); + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + SET_ENABLE( ImeEnumRegisterWord, TRUE ); + + SetLastError( 0xdeadbeef ); + ok_ret( 0, ImmEnumRegisterWordW( NULL, enum_register_wordW, NULL, 0, NULL, NULL ) ); + ok_ret( 0, ImmEnumRegisterWordA( NULL, enum_register_wordA, NULL, 0, NULL, NULL ) ); + ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) ); + ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) ); + todo_wine + ok_ret( 0xdeadbeef, GetLastError() ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + SET_EXPECT( ImeEnumRegisterWord ); + ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) ); + CHECK_CALLED( ImeEnumRegisterWord ); + + SET_EXPECT( ImeEnumRegisterWord ); + ok_ret( 0, ImmEnumRegisterWordA( hkl, enum_register_wordA, NULL, 0, NULL, NULL ) ); + CHECK_CALLED( ImeEnumRegisterWord ); + + SET_EXPECT( ImeEnumRegisterWord ); + ok_ret( 0xdeadbeef, ImmEnumRegisterWordW( hkl, enum_register_wordW, L"Reading", 0xdeadbeef, L"String", NULL ) ); + CHECK_CALLED( ImeEnumRegisterWord ); + + SET_EXPECT( ImeEnumRegisterWord ); + ok_ret( 0xdeadbeef, ImmEnumRegisterWordA( hkl, enum_register_wordA, "Reading", 0xdeadbeef, "String", NULL ) ); + CHECK_CALLED( ImeEnumRegisterWord ); + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( ImeEnumRegisterWord, FALSE ); + + winetest_pop_context(); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -3310,6 +3404,8 @@ START_TEST(imm32) test_ImmEscape( FALSE ); test_ImmEscape( TRUE ); + test_ImmEnumRegisterWord( FALSE ); + test_ImmEnumRegisterWord( TRUE ); if (init()) { From 4e05e3d95e0a9040dc797c48720999f1c8d54d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Feb 2023 11:55:56 +0100 Subject: [PATCH 128/758] imm32/tests: Test ImmRegisterWord with the installed IME. (cherry picked from commit eab785a358debb4a08c0657df6cd097fed6b4bcc) --- dlls/imm32/tests/imm32.c | 81 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 4feb423f84e..31515a8f99a 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2465,6 +2465,7 @@ static BOOL todo_ImeDestroy; DEFINE_EXPECT( ImeDestroy ); DEFINE_EXPECT( ImeEscape ); DEFINE_EXPECT( ImeEnumRegisterWord ); +DEFINE_EXPECT( ImeRegisterWord ); static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); static BOOL todo_IME_DLL_PROCESS_DETACH; @@ -2635,7 +2636,21 @@ static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYT static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) { ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) ); - ok( 0, "unexpected call\n" ); + + CHECK_EXPECT( ImeRegisterWord ); + + if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" ); + if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + if (reading) ok_wcs( L"Reading", reading ); + if (string) ok_wcs( L"String", string ); + } + else + { + if (reading) ok_str( "Reading", (char *)reading ); + if (string) ok_str( "String", (char *)string ); + } + return FALSE; } @@ -3386,6 +3401,68 @@ static void test_ImmEnumRegisterWord( BOOL unicode ) winetest_pop_context(); } +static void test_ImmRegisterWord( BOOL unicode ) +{ + HKL hkl = GetKeyboardLayout( 0 ); + + SET_ENABLE( ImeRegisterWord, TRUE ); + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + SetLastError( 0xdeadbeef ); + ok_ret( 0, ImmRegisterWordW( NULL, NULL, 0, NULL ) ); + ok_ret( 0, ImmRegisterWordA( NULL, NULL, 0, NULL ) ); + ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) ); + ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) ); + todo_wine + ok_ret( 0xdeadbeef, GetLastError() ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, NULL ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordW( hkl, L"Reading", 0, NULL ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordA( hkl, "Reading", 0, NULL ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0xdeadbeef, NULL ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0xdeadbeef, NULL ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, L"String" ) ); + CHECK_CALLED( ImeRegisterWord ); + + SET_EXPECT( ImeRegisterWord ); + ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, "String" ) ); + CHECK_CALLED( ImeRegisterWord ); + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( ImeRegisterWord, FALSE ); + + winetest_pop_context(); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -3406,6 +3483,8 @@ START_TEST(imm32) test_ImmEscape( TRUE ); test_ImmEnumRegisterWord( FALSE ); test_ImmEnumRegisterWord( TRUE ); + test_ImmRegisterWord( FALSE ); + test_ImmRegisterWord( TRUE ); if (init()) { From 7ab34a3662a84fd5e5d5f1d55cf6cb441b8346f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Feb 2023 11:58:44 +0100 Subject: [PATCH 129/758] imm32/tests: Test ImmGetRegisterWordStyle with the installed IME. (cherry picked from commit 6896953d95bbb103b04221e321a91ce7343018ba) --- dlls/imm32/tests/imm32.c | 101 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 31515a8f99a..faeb9f19a2b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2466,6 +2466,7 @@ DEFINE_EXPECT( ImeDestroy ); DEFINE_EXPECT( ImeEscape ); DEFINE_EXPECT( ImeEnumRegisterWord ); DEFINE_EXPECT( ImeRegisterWord ); +DEFINE_EXPECT( ImeGetRegisterWordStyle ); static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); static BOOL todo_IME_DLL_PROCESS_DETACH; @@ -2599,8 +2600,25 @@ static DWORD WINAPI ime_ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style ) { ime_trace( "item %u, style %p\n", item, style ); - ok( 0, "unexpected call\n" ); - return 0; + + CHECK_EXPECT( ImeGetRegisterWordStyle ); + + if (!style) + ok_eq( 16, item, UINT, "%u" ); + else if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + STYLEBUFW *styleW = style; + styleW->dwStyle = 0xdeadbeef; + wcscpy( styleW->szDescription, L"StyleDescription" ); + } + else + { + STYLEBUFA *styleA = (STYLEBUFA *)style; + styleA->dwStyle = 0xdeadbeef; + strcpy( styleA->szDescription, "StyleDescription" ); + } + + return 0xdeadbeef; } static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) @@ -3463,6 +3481,83 @@ static void test_ImmRegisterWord( BOOL unicode ) winetest_pop_context(); } +static void test_ImmGetRegisterWordStyle( BOOL unicode ) +{ + HKL hkl = GetKeyboardLayout( 0 ); + STYLEBUFW styleW; + STYLEBUFA styleA; + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + SET_ENABLE( ImeGetRegisterWordStyle, TRUE ); + + SetLastError( 0xdeadbeef ); + ok_ret( 0, ImmGetRegisterWordStyleW( NULL, 0, &styleW ) ); + ok_ret( 0, ImmGetRegisterWordStyleA( NULL, 0, &styleA ) ); + ok_ret( 0, ImmGetRegisterWordStyleW( hkl, 0, &styleW ) ); + ok_ret( 0, ImmGetRegisterWordStyleA( hkl, 0, &styleA ) ); + todo_wine + ok_ret( 0xdeadbeef, GetLastError() ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + if (!strcmp( winetest_platform, "wine" )) goto skip_null; + + SET_EXPECT( ImeGetRegisterWordStyle ); + ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 16, NULL ) ); + CHECK_CALLED( ImeGetRegisterWordStyle ); + + SET_EXPECT( ImeGetRegisterWordStyle ); + ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 16, NULL ) ); + CHECK_CALLED( ImeGetRegisterWordStyle ); + +skip_null: + SET_EXPECT( ImeGetRegisterWordStyle ); + memset( &styleW, 0xcd, sizeof(styleW) ); + ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleW( hkl, 1, &styleW ) ); + if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + ok_eq( 0xdeadbeef, styleW.dwStyle, UINT, "%#x" ); + ok_wcs( L"StyleDescription", styleW.szDescription ); + } + else + { + todo_wine + ok_eq( 0xcdcdcdcd, styleW.dwStyle, UINT, "%#x" ); + todo_wine + ok_eq( 0xcdcd, styleW.szDescription[0], WORD, "%#x" ); + } + CHECK_CALLED( ImeGetRegisterWordStyle ); + + SET_EXPECT( ImeGetRegisterWordStyle ); + memset( &styleA, 0xcd, sizeof(styleA) ); + ok_ret( 0xdeadbeef, ImmGetRegisterWordStyleA( hkl, 1, &styleA ) ); + if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + todo_wine + ok_eq( 0xcdcdcdcd, styleA.dwStyle, UINT, "%#x" ); + todo_wine + ok_eq( 0xcd, styleA.szDescription[0], BYTE, "%#x" ); + } + else + { + ok_eq( 0xdeadbeef, styleA.dwStyle, UINT, "%#x" ); + ok_str( "StyleDescription", styleA.szDescription ); + } + CHECK_CALLED( ImeGetRegisterWordStyle ); + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( ImeGetRegisterWordStyle, FALSE ); + + winetest_pop_context(); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -3485,6 +3580,8 @@ START_TEST(imm32) test_ImmEnumRegisterWord( TRUE ); test_ImmRegisterWord( FALSE ); test_ImmRegisterWord( TRUE ); + test_ImmGetRegisterWordStyle( FALSE ); + test_ImmGetRegisterWordStyle( TRUE ); if (init()) { From de7ad93ee2f98cae0848b039a7561989c8803802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Feb 2023 11:59:37 +0100 Subject: [PATCH 130/758] imm32/tests: Test ImmUnregisterWord with the installed IME. (cherry picked from commit 195879ada8531b5124b26ef0eda65279a8c3942a) --- dlls/imm32/tests/imm32.c | 81 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index faeb9f19a2b..7f76c59a409 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2467,6 +2467,7 @@ DEFINE_EXPECT( ImeEscape ); DEFINE_EXPECT( ImeEnumRegisterWord ); DEFINE_EXPECT( ImeRegisterWord ); DEFINE_EXPECT( ImeGetRegisterWordStyle ); +DEFINE_EXPECT( ImeUnregisterWord ); static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); static BOOL todo_IME_DLL_PROCESS_DETACH; @@ -2706,7 +2707,21 @@ static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) { ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) ); - ok( 0, "unexpected call\n" ); + + CHECK_EXPECT( ImeUnregisterWord ); + + if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" ); + if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + if (reading) ok_wcs( L"Reading", reading ); + if (string) ok_wcs( L"String", string ); + } + else + { + if (reading) ok_str( "Reading", (char *)reading ); + if (string) ok_str( "String", (char *)string ); + } + return FALSE; } @@ -3558,6 +3573,68 @@ static void test_ImmGetRegisterWordStyle( BOOL unicode ) winetest_pop_context(); } +static void test_ImmUnregisterWord( BOOL unicode ) +{ + HKL hkl = GetKeyboardLayout( 0 ); + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + SET_ENABLE( ImeUnregisterWord, TRUE ); + + SetLastError( 0xdeadbeef ); + ok_ret( 0, ImmUnregisterWordW( NULL, NULL, 0, NULL ) ); + ok_ret( 0, ImmUnregisterWordA( NULL, NULL, 0, NULL ) ); + ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) ); + ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) ); + todo_wine + ok_ret( 0xdeadbeef, GetLastError() ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, NULL ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordW( hkl, L"Reading", 0, NULL ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordA( hkl, "Reading", 0, NULL ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0xdeadbeef, NULL ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0xdeadbeef, NULL ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, L"String" ) ); + CHECK_CALLED( ImeUnregisterWord ); + + SET_EXPECT( ImeUnregisterWord ); + ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, "String" ) ); + CHECK_CALLED( ImeUnregisterWord ); + + ime_cleanup( hkl ); + +cleanup: + SET_ENABLE( ImeUnregisterWord, FALSE ); + + winetest_pop_context(); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -3582,6 +3659,8 @@ START_TEST(imm32) test_ImmRegisterWord( TRUE ); test_ImmGetRegisterWordStyle( FALSE ); test_ImmGetRegisterWordStyle( TRUE ); + test_ImmUnregisterWord( FALSE ); + test_ImmUnregisterWord( TRUE ); if (init()) { From 72c9aad7211633453449ec9365dff9896a1bd744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 17:12:55 +0100 Subject: [PATCH 131/758] imm32/tests: Test basic ImmEnumInputContext usage. (cherry picked from commit 196295db88237b084f1825e0c9b0e4d3cf6afedd) --- dlls/imm32/tests/imm32.c | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 7f76c59a409..015d6391520 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -55,6 +55,12 @@ static const char *debugstr_ok( const char *cond ) t v = (r); \ ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ } while (0) +#define ok_ne( e, r, t, f, ... ) \ + do \ + { \ + t v = (r); \ + ok( v != (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ + } while (0) #define ok_wcs( e, r ) \ do \ { \ @@ -2779,6 +2785,7 @@ static struct ime_functions ime_functions = static UINT ime_count; static WCHAR ime_path[MAX_PATH]; +static HIMC default_himc; static HKL ime_install(void) { @@ -2897,6 +2904,39 @@ static void ime_cleanup( HKL hkl ) ok( ret, "DeleteFileW failed, error %lu\n", GetLastError() ); } +static BOOL CALLBACK enum_get_context( HIMC himc, LPARAM lparam ) +{ + ime_trace( "himc %p\n", himc ); + *(HIMC *)lparam = himc; + return TRUE; +} + +static BOOL CALLBACK enum_find_context( HIMC himc, LPARAM lparam ) +{ + ime_trace( "himc %p\n", himc ); + if (lparam && lparam == (LPARAM)himc) return FALSE; + return TRUE; +} + +static void test_ImmEnumInputContext(void) +{ + HIMC himc; + + todo_wine + ok_ret( 1, ImmEnumInputContext( 0, enum_get_context, (LPARAM)&default_himc ) ); + ok_ret( 0, ImmEnumInputContext( 1, enum_find_context, 0 ) ); + todo_wine + ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, 0 ) ); + ok_ret( 0, ImmEnumInputContext( GetCurrentProcessId(), enum_find_context, 0 ) ); + + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ok_ret( 0, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + todo_wine + ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) ); +} + static void test_ImmInstallIME(void) { UINT ret; @@ -3645,6 +3685,8 @@ START_TEST(imm32) test_com_initialization(); + test_ImmEnumInputContext(); + test_ImmInstallIME(); test_ImmGetDescription(); test_ImmGetIMEFileName(); From 99974b1b5d9c032eb2c85e4b26c106e9930b3618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 08:53:29 +0100 Subject: [PATCH 132/758] win32u/tests: Test NtUserCreateInputContext (et al.) (cherry picked from commit a865ce8298721547af62ac53c6cecc179da65dc5) --- dlls/win32u/tests/win32u.c | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 60ef2d38b5f..51a67a697f5 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -221,6 +221,103 @@ static void test_class(void) } +static void test_NtUserCreateInputContext(void) +{ + UINT_PTR value, attr3; + HIMC himc; + UINT ret; + + SetLastError( 0xdeadbeef ); + himc = NtUserCreateInputContext( 0 ); + todo_wine + ok( !himc, "NtUserCreateInputContext succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + ret = NtUserDestroyInputContext( himc ); + todo_wine + ok( !ret, "NtUserDestroyInputContext succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_INVALID_HANDLE, "got error %lu\n", GetLastError() ); + + + himc = NtUserCreateInputContext( 0xdeadbeef ); + ok( !!himc, "NtUserCreateInputContext failed, error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 0 ); + todo_wine + ok( value == GetCurrentProcessId(), "NtUserQueryInputContext 0 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 1 ); + ok( value == GetCurrentThreadId(), "NtUserQueryInputContext 1 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 2 ); + ok( value == 0, "NtUserQueryInputContext 2 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 3 ); + todo_wine + ok( !!value, "NtUserQueryInputContext 3 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + attr3 = value; + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 4 ); + todo_wine + ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + ret = NtUserUpdateInputContext( himc, 0, 0 ); + todo_wine + ok( !ret, "NtUserUpdateInputContext 0 succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_ALREADY_INITIALIZED, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + ret = NtUserUpdateInputContext( himc, 1, 0xdeadbeef ); + todo_wine + ok( !!ret, "NtUserUpdateInputContext 1 failed\n" ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + ret = NtUserUpdateInputContext( himc, 2, 0xdeadbeef ); + ok( !ret, "NtUserUpdateInputContext 2 succeeded\n" ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + ret = NtUserUpdateInputContext( himc, 3, 0x0badf00d ); + ok( !ret, "NtUserUpdateInputContext 3 succeeded\n" ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + ret = NtUserUpdateInputContext( himc, 4, 0xdeadbeef ); + ok( !ret, "NtUserUpdateInputContext 4 succeeded\n" ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 0 ); + todo_wine + ok( value == GetCurrentProcessId(), "NtUserQueryInputContext 0 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 1 ); + ok( value == GetCurrentThreadId(), "NtUserQueryInputContext 1 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 2 ); + ok( value == 0, "NtUserQueryInputContext 2 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 3 ); + ok( value == attr3, "NtUserQueryInputContext 3 returned %#Ix\n", value ); + ok( GetLastError() == 0xdeadbeef, "got error %lu\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + value = NtUserQueryInputContext( himc, 4 ); + todo_wine + ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() ); + + ret = NtUserDestroyInputContext( himc ); + ok( !!ret, "NtUserDestroyInputContext failed, error %lu\n", GetLastError() ); +} + static BOOL WINAPI count_win( HWND hwnd, LPARAM lparam ) { ULONG *cnt = (ULONG *)lparam; @@ -1148,6 +1245,7 @@ START_TEST(win32u) test_NtUserEnumDisplayDevices(); test_window_props(); test_class(); + test_NtUserCreateInputContext(); test_NtUserBuildHwndList(); test_cursoricon(); test_message_call(); From 7466147ef51ef9a50c73be5eb2c7e114ac6fa2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 07:54:45 +0100 Subject: [PATCH 133/758] win32u: Stub NtUserBuildHimcList syscall. (cherry picked from commit 7ecb1446568ac6f1511f16956a2576dc0a73fce3) --- dlls/win32u/imm.c | 11 +++++++++++ dlls/win32u/syscall.c | 1 + dlls/win32u/win32u.spec | 2 +- dlls/wow64win/syscall.h | 1 + dlls/wow64win/user.c | 19 +++++++++++++++++++ include/ntuser.h | 1 + 6 files changed, 34 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index 1ccc09c97b2..dc10eedd903 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -25,6 +25,8 @@ #endif #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "win32u_private.h" #include "ntuser_private.h" #include "immdev.h" @@ -393,6 +395,15 @@ void cleanup_imm_thread(void) NtUserDestroyInputContext( UlongToHandle( thread_info->client_info.default_imc )); } +/***************************************************************************** + * NtUserBuildHimcList (win32u.@) + */ +NTSTATUS WINAPI NtUserBuildHimcList( UINT thread_id, UINT count, HIMC *buffer, UINT *size ) +{ + FIXME( "thread_id %#x, count %u, buffer %p, size %p stub!\n", thread_id, count, buffer, size ); + return STATUS_NOT_IMPLEMENTED; +} + BOOL WINAPI DECLSPEC_HIDDEN ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM key_data, DWORD unknown ) { struct imm_process_key_params params = diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c index e802b3d2ec0..2e39a4f62cc 100644 --- a/dlls/win32u/syscall.c +++ b/dlls/win32u/syscall.c @@ -106,6 +106,7 @@ static void * const syscalls[] = NtUserAssociateInputContext, NtUserAttachThreadInput, NtUserBeginPaint, + NtUserBuildHimcList, NtUserBuildHwndList, NtUserCallHwnd, NtUserCallHwndParam, diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec index d450d07635e..764eb38d862 100644 --- a/dlls/win32u/win32u.spec +++ b/dlls/win32u/win32u.spec @@ -762,7 +762,7 @@ @ stub NtUserBitBltSysBmp @ stub NtUserBlockInput @ stub NtUserBroadcastThemeChangeEvent -@ stub NtUserBuildHimcList +@ stdcall -syscall NtUserBuildHimcList(long long ptr ptr) @ stdcall -syscall NtUserBuildHwndList(long long long long long long ptr ptr) @ stub NtUserBuildNameList @ stub NtUserBuildPropList diff --git a/dlls/wow64win/syscall.h b/dlls/wow64win/syscall.h index 8543c877644..c9dc459709e 100644 --- a/dlls/wow64win/syscall.h +++ b/dlls/wow64win/syscall.h @@ -92,6 +92,7 @@ SYSCALL_ENTRY( NtUserAssociateInputContext ) \ SYSCALL_ENTRY( NtUserAttachThreadInput ) \ SYSCALL_ENTRY( NtUserBeginPaint ) \ + SYSCALL_ENTRY( NtUserBuildHimcList ) \ SYSCALL_ENTRY( NtUserBuildHwndList ) \ SYSCALL_ENTRY( NtUserCallHwnd ) \ SYSCALL_ENTRY( NtUserCallHwndParam ) \ diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c index c8ce23a05ce..458f5cd0788 100644 --- a/dlls/wow64win/user.c +++ b/dlls/wow64win/user.c @@ -1182,6 +1182,25 @@ NTSTATUS WINAPI wow64_NtUserBeginPaint( UINT *args ) return HandleToUlong( ret ); } +NTSTATUS WINAPI wow64_NtUserBuildHimcList( UINT *args ) +{ + ULONG thread_id = get_ulong( &args ); + ULONG count = get_ulong( &args ); + UINT32 *buffer32 = get_ptr( &args ); + UINT *size = get_ptr( &args ); + + HIMC *buffer; + ULONG i; + NTSTATUS status; + + if (!(buffer = Wow64AllocateTemp( count * sizeof(*buffer) ))) return STATUS_NO_MEMORY; + + if ((status = NtUserBuildHimcList( thread_id, count, buffer, size ))) return status; + + for (i = 0; i < *size; i++) buffer32[i] = HandleToUlong( buffer[i] ); + return status; +} + NTSTATUS WINAPI wow64_NtUserBuildHwndList( UINT *args ) { HDESK desktop = get_handle( &args ); diff --git a/include/ntuser.h b/include/ntuser.h index 5295c2c2108..0771814c999 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -648,6 +648,7 @@ BOOL WINAPI NtUserAddClipboardFormatListener( HWND hwnd ); UINT WINAPI NtUserAssociateInputContext( HWND hwnd, HIMC ctx, ULONG flags ); BOOL WINAPI NtUserAttachThreadInput( DWORD from, DWORD to, BOOL attach ); HDC WINAPI NtUserBeginPaint( HWND hwnd, PAINTSTRUCT *ps ); +NTSTATUS WINAPI NtUserBuildHimcList( UINT thread_id, UINT count, HIMC *buffer, UINT *size ); NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, ULONG unk2, ULONG unk3, ULONG unk4, ULONG thread_id, ULONG count, HWND *buffer, ULONG *size ); ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code ); From 1f3376ac965bce32d04733464d6ff56999adb38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 08:58:33 +0100 Subject: [PATCH 134/758] win32u/tests: Test NtUserBuildHimcList syscall. (cherry picked from commit 8dab5e32ab989b3b789f8f9acae2b74c5f0e234e) --- dlls/win32u/tests/win32u.c | 134 +++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 51a67a697f5..975716045cf 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -318,6 +318,139 @@ static void test_NtUserCreateInputContext(void) ok( !!ret, "NtUserDestroyInputContext failed, error %lu\n", GetLastError() ); } +static int himc_compare( const void *a, const void *b ) +{ + return (UINT_PTR)*(HIMC *)a - (UINT_PTR)*(HIMC *)b; +} + +static DWORD CALLBACK test_NtUserBuildHimcList_thread( void *arg ) +{ + HIMC buf[8], *himc = arg; + NTSTATUS status; + UINT size; + + size = 0xdeadbeef; + memset( buf, 0xcd, sizeof(buf) ); + status = NtUserBuildHimcList( GetCurrentThreadId(), ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + todo_wine + ok( size == 1, "size = %u\n", size ); + ok( !!buf[0], "buf[0] = %p\n", buf[0] ); + + todo_wine + ok( buf[0] != himc[0], "buf[0] = %p\n", buf[0] ); + ok( buf[0] != himc[1], "buf[0] = %p\n", buf[0] ); + himc[2] = buf[0]; + qsort( himc, 3, sizeof(*himc), himc_compare ); + + size = 0xdeadbeef; + memset( buf, 0xcd, sizeof(buf) ); + status = NtUserBuildHimcList( -1, ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + todo_wine + ok( size == 3, "size = %u\n", size ); + + qsort( buf, size, sizeof(*buf), himc_compare ); + ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); + ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); + todo_wine + ok( buf[2] == himc[2], "buf[2] = %p\n", buf[2] ); + + return 0; +} + +static void test_NtUserBuildHimcList(void) +{ + HIMC buf[8], himc[3], new_himc; + NTSTATUS status; + UINT size, ret; + HANDLE thread; + + size = 0xdeadbeef; + memset( buf, 0xcd, sizeof(buf) ); + status = NtUserBuildHimcList( GetCurrentThreadId(), ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + todo_wine + ok( size == 1, "size = %u\n", size ); + ok( !!buf[0], "buf[0] = %p\n", buf[0] ); + himc[0] = buf[0]; + + + new_himc = NtUserCreateInputContext( 0xdeadbeef ); + ok( !!new_himc, "NtUserCreateInputContext failed, error %lu\n", GetLastError() ); + + himc[1] = new_himc; + qsort( himc, 2, sizeof(*himc), himc_compare ); + + size = 0xdeadbeef; + memset( buf, 0xcd, sizeof(buf) ); + status = NtUserBuildHimcList( GetCurrentThreadId(), ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + todo_wine + ok( size == 2, "size = %u\n", size ); + + qsort( buf, size, sizeof(*buf), himc_compare ); + ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); + todo_wine + ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); + + size = 0xdeadbeef; + memset( buf, 0xcd, sizeof(buf) ); + status = NtUserBuildHimcList( 0, ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + todo_wine + ok( size == 2, "size = %u\n", size ); + + qsort( buf, size, sizeof(*buf), himc_compare ); + ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); + todo_wine + ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); + + size = 0xdeadbeef; + memset( buf, 0xcd, sizeof(buf) ); + status = NtUserBuildHimcList( -1, ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + todo_wine + ok( size == 2, "size = %u\n", size ); + + qsort( buf, size, sizeof(*buf), himc_compare ); + ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); + todo_wine + ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); + + thread = CreateThread( NULL, 0, test_NtUserBuildHimcList_thread, himc, 0, NULL ); + ok( !!thread, "CreateThread failed, error %lu\n", GetLastError() ); + ret = WaitForSingleObject( thread, 5000 ); + ok( !ret, "WaitForSingleObject returned %#x\n", ret ); + + size = 0xdeadbeef; + status = NtUserBuildHimcList( 1, ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( status == STATUS_INVALID_PARAMETER, "NtUserBuildHimcList returned %#lx\n", status ); + size = 0xdeadbeef; + status = NtUserBuildHimcList( GetCurrentProcessId(), ARRAYSIZE( buf ), buf, &size ); + todo_wine + ok( status == STATUS_INVALID_PARAMETER, "NtUserBuildHimcList returned %#lx\n", status ); + size = 0xdeadbeef; + status = NtUserBuildHimcList( GetCurrentThreadId(), 1, NULL, &size ); + todo_wine + ok( status == STATUS_UNSUCCESSFUL, "NtUserBuildHimcList returned %#lx\n", status ); + size = 0xdeadbeef; + status = NtUserBuildHimcList( GetCurrentThreadId(), 0, buf, &size ); + todo_wine + ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); + ok( size == 0, "size = %u\n", size ); + + ret = NtUserDestroyInputContext( new_himc ); + ok( !!ret, "NtUserDestroyInputContext failed, error %lu\n", GetLastError() ); +} + static BOOL WINAPI count_win( HWND hwnd, LPARAM lparam ) { ULONG *cnt = (ULONG *)lparam; @@ -1246,6 +1379,7 @@ START_TEST(win32u) test_window_props(); test_class(); test_NtUserCreateInputContext(); + test_NtUserBuildHimcList(); test_NtUserBuildHwndList(); test_cursoricon(); test_message_call(); From 21878fcddf31b5b7f5a016c400a5db9579c5e085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 09:24:35 +0100 Subject: [PATCH 135/758] win32u: Introduce new next_process_user_handle_ptr helper. And use it instead of next_thread_window_ptr. (cherry picked from commit 7297a8e69c5446694b2c0cc05918b4da15f01426) --- dlls/win32u/ntuser_private.h | 6 +---- dlls/win32u/window.c | 48 ++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 8ab1b35291e..41a53837642 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -52,11 +52,6 @@ struct user_object #define OBJ_OTHER_PROCESS ((void *)1) /* returned by get_user_handle_ptr on unknown handles */ -HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type ) DECLSPEC_HIDDEN; -void *get_user_handle_ptr( HANDLE handle, unsigned int type ) DECLSPEC_HIDDEN; -void release_user_handle_ptr( void *ptr ) DECLSPEC_HIDDEN; -void *free_user_handle( HANDLE handle, unsigned int type ) DECLSPEC_HIDDEN; - typedef struct tagWND { struct user_object obj; /* object header */ @@ -253,6 +248,7 @@ HANDLE alloc_user_handle( struct user_object *ptr, unsigned int type ) DECLSPEC_ void *free_user_handle( HANDLE handle, unsigned int type ) DECLSPEC_HIDDEN; void *get_user_handle_ptr( HANDLE handle, unsigned int type ) DECLSPEC_HIDDEN; void release_user_handle_ptr( void *ptr ) DECLSPEC_HIDDEN; +void *next_process_user_handle_ptr( HANDLE *handle, unsigned int type ) DECLSPEC_HIDDEN; UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask ) DECLSPEC_HIDDEN; /* winstation.c */ diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index a07be55e883..333fde4b472 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -101,6 +101,26 @@ void *get_user_handle_ptr( HANDLE handle, unsigned int type ) return ptr; } +/*********************************************************************** + * next_process_user_handle_ptr + * + * user_lock must be held by caller. + */ +void *next_process_user_handle_ptr( HANDLE *handle, unsigned int type ) +{ + struct user_object *ptr; + WORD index = *handle ? USER_HANDLE_TO_INDEX( *handle ) + 1 : 0; + + while (index < NB_USER_HANDLES) + { + if (!(ptr = user_handles[index++])) continue; /* OBJ_OTHER_PROCESS */ + if (ptr->type != type) continue; + *handle = ptr->handle; + return ptr; + } + return NULL; +} + /*********************************************************************** * set_user_handle_ptr */ @@ -142,27 +162,6 @@ void *free_user_handle( HANDLE handle, unsigned int type ) return ptr; } -/*********************************************************************** - * next_thread_window - */ -static WND *next_thread_window_ptr( HWND *hwnd ) -{ - struct user_object *ptr; - WND *win; - WORD index = *hwnd ? USER_HANDLE_TO_INDEX( *hwnd ) + 1 : 0; - - while (index < NB_USER_HANDLES) - { - if (!(ptr = user_handles[index++])) continue; - if (ptr->type != NTUSER_OBJ_WINDOW) continue; - win = (WND *)ptr; - if (win->tid != GetCurrentThreadId()) continue; - *hwnd = ptr->handle; - return win; - } - return NULL; -} - /******************************************************************* * get_hwnd_message_parent * @@ -4865,13 +4864,14 @@ BOOL WINAPI NtUserDestroyWindow( HWND hwnd ) void destroy_thread_windows(void) { WND *win, *free_list = NULL; - HWND hwnd = 0; + HANDLE handle = 0; user_lock(); - while ((win = next_thread_window_ptr( &hwnd ))) + while ((win = next_process_user_handle_ptr( &handle, NTUSER_OBJ_WINDOW ))) { + if (win->tid != GetCurrentThreadId()) continue; free_dce( win->dce, win->obj.handle ); - set_user_handle_ptr( hwnd, NULL ); + set_user_handle_ptr( handle, NULL ); win->obj.handle = free_list; free_list = win; } From 221867379111f11d923ac036ef989a30d0059b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Mar 2023 14:54:25 +0100 Subject: [PATCH 136/758] win32u: Implement NtUserBuildHimcList syscall. (cherry picked from commit ac95a5d48395ffab79f2a7189b0d5ba21f473658) --- dlls/win32u/imm.c | 21 +++++++++++++++++++-- dlls/win32u/tests/win32u.c | 19 +++---------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index dc10eedd903..7dee4912e27 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -400,8 +400,25 @@ void cleanup_imm_thread(void) */ NTSTATUS WINAPI NtUserBuildHimcList( UINT thread_id, UINT count, HIMC *buffer, UINT *size ) { - FIXME( "thread_id %#x, count %u, buffer %p, size %p stub!\n", thread_id, count, buffer, size ); - return STATUS_NOT_IMPLEMENTED; + HANDLE handle = 0; + struct imc *imc; + + TRACE( "thread_id %#x, count %u, buffer %p, size %p\n", thread_id, count, buffer, size ); + + if (!buffer) return STATUS_UNSUCCESSFUL; + if (!thread_id) thread_id = GetCurrentThreadId(); + + *size = 0; + user_lock(); + while (count && (imc = next_process_user_handle_ptr( &handle, NTUSER_OBJ_IMC ))) + { + if (thread_id != -1 && imc->thread_id != thread_id) continue; + buffer[(*size)++] = handle; + count--; + } + user_unlock(); + + return STATUS_SUCCESS; } BOOL WINAPI DECLSPEC_HIDDEN ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM key_data, DWORD unknown ) diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 975716045cf..04e5a2e7932 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -332,13 +332,11 @@ static DWORD CALLBACK test_NtUserBuildHimcList_thread( void *arg ) size = 0xdeadbeef; memset( buf, 0xcd, sizeof(buf) ); status = NtUserBuildHimcList( GetCurrentThreadId(), ARRAYSIZE( buf ), buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); todo_wine ok( size == 1, "size = %u\n", size ); ok( !!buf[0], "buf[0] = %p\n", buf[0] ); - todo_wine ok( buf[0] != himc[0], "buf[0] = %p\n", buf[0] ); ok( buf[0] != himc[1], "buf[0] = %p\n", buf[0] ); himc[2] = buf[0]; @@ -347,13 +345,15 @@ static DWORD CALLBACK test_NtUserBuildHimcList_thread( void *arg ) size = 0xdeadbeef; memset( buf, 0xcd, sizeof(buf) ); status = NtUserBuildHimcList( -1, ARRAYSIZE( buf ), buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); todo_wine ok( size == 3, "size = %u\n", size ); qsort( buf, size, sizeof(*buf), himc_compare ); + /* FIXME: Wine only lazily creates a default thread IMC */ + todo_wine ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); + todo_wine ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); todo_wine ok( buf[2] == himc[2], "buf[2] = %p\n", buf[2] ); @@ -371,9 +371,7 @@ static void test_NtUserBuildHimcList(void) size = 0xdeadbeef; memset( buf, 0xcd, sizeof(buf) ); status = NtUserBuildHimcList( GetCurrentThreadId(), ARRAYSIZE( buf ), buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); - todo_wine ok( size == 1, "size = %u\n", size ); ok( !!buf[0], "buf[0] = %p\n", buf[0] ); himc[0] = buf[0]; @@ -388,40 +386,31 @@ static void test_NtUserBuildHimcList(void) size = 0xdeadbeef; memset( buf, 0xcd, sizeof(buf) ); status = NtUserBuildHimcList( GetCurrentThreadId(), ARRAYSIZE( buf ), buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); - todo_wine ok( size == 2, "size = %u\n", size ); qsort( buf, size, sizeof(*buf), himc_compare ); ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); - todo_wine ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); size = 0xdeadbeef; memset( buf, 0xcd, sizeof(buf) ); status = NtUserBuildHimcList( 0, ARRAYSIZE( buf ), buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); - todo_wine ok( size == 2, "size = %u\n", size ); qsort( buf, size, sizeof(*buf), himc_compare ); ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); - todo_wine ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); size = 0xdeadbeef; memset( buf, 0xcd, sizeof(buf) ); status = NtUserBuildHimcList( -1, ARRAYSIZE( buf ), buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); - todo_wine ok( size == 2, "size = %u\n", size ); qsort( buf, size, sizeof(*buf), himc_compare ); ok( buf[0] == himc[0], "buf[0] = %p\n", buf[0] ); - todo_wine ok( buf[1] == himc[1], "buf[1] = %p\n", buf[1] ); thread = CreateThread( NULL, 0, test_NtUserBuildHimcList_thread, himc, 0, NULL ); @@ -439,11 +428,9 @@ static void test_NtUserBuildHimcList(void) ok( status == STATUS_INVALID_PARAMETER, "NtUserBuildHimcList returned %#lx\n", status ); size = 0xdeadbeef; status = NtUserBuildHimcList( GetCurrentThreadId(), 1, NULL, &size ); - todo_wine ok( status == STATUS_UNSUCCESSFUL, "NtUserBuildHimcList returned %#lx\n", status ); size = 0xdeadbeef; status = NtUserBuildHimcList( GetCurrentThreadId(), 0, buf, &size ); - todo_wine ok( !status, "NtUserBuildHimcList failed: %#lx\n", status ); ok( size == 0, "size = %u\n", size ); From 1b1c1174713df1a8d1f7ad7d3dc76a8ec9a16644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Mar 2023 09:03:16 +0100 Subject: [PATCH 137/758] imm32/tests: Remove GetLastError check on default IME ImmEscape tests. Wine-Bug: https://bugs.winehq.org//show_bug.cgi?id=54710 (cherry picked from commit 9ab5fb591fcfbe53bab16754adacc2adf0d27b24) --- dlls/imm32/tests/imm32.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 015d6391520..233fda84b61 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3313,11 +3313,8 @@ static void test_ImmEscape( BOOL unicode ) winetest_push_context( unicode ? "unicode" : "ansi" ); - SetLastError( 0xdeadbeef ); ok_ret( 0, ImmEscapeW( hkl, 0, 0, NULL ) ); ok_ret( 0, ImmEscapeA( hkl, 0, 0, NULL ) ); - todo_wine - ok_ret( 0xdeadbeef, GetLastError() ); /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ ime_info.fdwProperty = IME_PROP_END_UNLOAD; From f32b2bf82227fe6b265c46812443fc7e5086abff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Mar 2023 09:05:26 +0100 Subject: [PATCH 138/758] imm32/tests: Update ImmGetProperty expectations for Korean locale. Wine-Bug: https://bugs.winehq.org//show_bug.cgi?id=54711 (cherry picked from commit 067b81fcfa82cd442b0c2542ff4e902fe19869a2) --- dlls/imm32/tests/imm32.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 233fda84b61..5c4d1d0725c 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3054,7 +3054,7 @@ static void test_ImmGetProperty(void) }; static const IMEINFO expect_ime_info_0411 = /* MS Japanese IME */ { - .fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa, + .fdwProperty = IME_PROP_CANDLIST_START_FROM_1 | IME_PROP_UNICODE | IME_PROP_AT_CARET | 0xa, .fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA, .fdwSentenceCaps = IME_SMODE_PLAURALCLAUSE | IME_SMODE_CONVERSATION, .fdwSCSCaps = SCS_CAP_COMPSTR | SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD, @@ -3093,7 +3093,8 @@ static void test_ImmGetProperty(void) else if (hkl == (HKL)0x08040804) expect = &expect_ime_info_0804; else expect = &expect_ime_info; - ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) ); + /* IME_PROP_COMPLETE_ON_UNSELECT seems to be somtimes set on CJK locales IMEs, sometimes not */ + ok_ret( expect->fdwProperty, ImmGetProperty( hkl, IGP_PROPERTY ) & ~IME_PROP_COMPLETE_ON_UNSELECT ); todo_wine ok_ret( expect->fdwConversionCaps, ImmGetProperty( hkl, IGP_CONVERSION ) ); todo_wine From 96b86e5016588660ab696c5af34d75c9a039d046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 10:15:05 +0100 Subject: [PATCH 139/758] imm32: Implement ImmEnumInputContext. (cherry picked from commit a7c2f4e5bac800785b1584d9ab48b74336a35dd5) --- dlls/imm32/imm.c | 21 ++++++++++++++++++--- dlls/imm32/tests/imm32.c | 7 ++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 682fb748f2f..28344b909d6 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3033,10 +3033,25 @@ BOOL WINAPI ImmDisableTextFrameService(DWORD idThread) * ImmEnumInputContext(IMM32.@) */ -BOOL WINAPI ImmEnumInputContext(DWORD idThread, IMCENUMPROC lpfn, LPARAM lParam) +BOOL WINAPI ImmEnumInputContext( DWORD thread, IMCENUMPROC callback, LPARAM lparam ) { - FIXME("Stub\n"); - return FALSE; + HIMC buffer[256]; + NTSTATUS status; + UINT i, size; + + TRACE( "thread %lu, callback %p, lparam %#Ix\n", thread, callback, lparam ); + + if ((status = NtUserBuildHimcList( thread, ARRAY_SIZE(buffer), buffer, &size ))) + { + RtlSetLastWin32Error( RtlNtStatusToDosError( status ) ); + WARN( "NtUserBuildHimcList returned %#lx\n", status ); + return FALSE; + } + + if (size == ARRAY_SIZE(buffer)) FIXME( "NtUserBuildHimcList returned %u handles\n", size ); + for (i = 0; i < size; i++) if (!callback( buffer[i], lparam )) return FALSE; + + return TRUE; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 5c4d1d0725c..73fbe9d1aa5 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2922,18 +2922,19 @@ static void test_ImmEnumInputContext(void) { HIMC himc; - todo_wine ok_ret( 1, ImmEnumInputContext( 0, enum_get_context, (LPARAM)&default_himc ) ); + ok_ret( 1, ImmEnumInputContext( -1, enum_find_context, 0 ) ); + ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, 0 ) ); + + todo_wine ok_ret( 0, ImmEnumInputContext( 1, enum_find_context, 0 ) ); todo_wine - ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, 0 ) ); ok_ret( 0, ImmEnumInputContext( GetCurrentProcessId(), enum_find_context, 0 ) ); himc = ImmCreateContext(); ok_ne( NULL, himc, HIMC, "%p" ); ok_ret( 0, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); - todo_wine ok_ret( 1, ImmEnumInputContext( GetCurrentThreadId(), enum_find_context, (LPARAM)himc ) ); } From 6f2e47ddd96ae410c6557db0bab31326a8e6a25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 12:13:28 +0100 Subject: [PATCH 140/758] imm32: Remove unnecessary threadDefault InputContextData member. (cherry picked from commit 29e51aa333ace47109fe46fc4b0f7e75e2293389) --- dlls/imm32/imm.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 28344b909d6..06d82671040 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -94,7 +94,6 @@ typedef struct tagInputContextData struct ime *ime; UINT lastVK; - BOOL threadDefault; } InputContextData; #define WINE_IMC_VALID_MAGIC 0x56434D49 @@ -404,16 +403,6 @@ static void imm_coinit_thread(void) InitOnceExecuteOnce(&init_ole32_once, init_ole32_funcs, NULL, NULL); } -static BOOL IMM_IsDefaultContext(HIMC imc) -{ - InputContextData *data = get_imc_data(imc); - - if (!data) - return FALSE; - - return data->threadDefault; -} - static InputContextData *query_imc_data(HIMC handle) { InputContextData *ret; @@ -868,7 +857,6 @@ static InputContextData *create_input_context(HIMC default_imc) new_context = calloc( 1, sizeof(InputContextData) ); /* Load the IME */ - new_context->threadDefault = !!default_imc; if (!(new_context->ime = ime_acquire( GetKeyboardLayout( 0 ) ))) { TRACE("IME dll could not be loaded\n"); @@ -953,7 +941,8 @@ static BOOL IMM_DestroyContext(HIMC hIMC) */ BOOL WINAPI ImmDestroyContext(HIMC hIMC) { - if (!IMM_IsDefaultContext(hIMC) && !IMM_IsCrossThreadAccess(NULL, hIMC)) + if ((UINT_PTR)hIMC == NtUserGetThreadInfo()->default_imc) return FALSE; + if (!IMM_IsCrossThreadAccess(NULL, hIMC)) return IMM_DestroyContext(hIMC); else return FALSE; From 353f19bf2c9f082e6d057f1584774e8736d96ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 12:08:49 +0100 Subject: [PATCH 141/758] imm32: Remove unused IMM_IsCrossThreadAccess hwnd parameter. (cherry picked from commit c66ea947a5ad7ccd106471488b6d3031e118df63) --- dlls/imm32/imm.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 06d82671040..26ec7c00140 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -682,15 +682,10 @@ static HIMCC ImmCreateBlankCompStr(void) return rc; } -static BOOL IMM_IsCrossThreadAccess(HWND hWnd, HIMC hIMC) +static BOOL IMM_IsCrossThreadAccess(HIMC hIMC) { InputContextData *data; - if (hWnd) - { - DWORD thread = GetWindowThreadProcessId(hWnd, NULL); - if (thread != GetCurrentThreadId()) return TRUE; - } data = get_imc_data(hIMC); if (data && data->threadID != GetCurrentThreadId()) return TRUE; @@ -942,7 +937,7 @@ static BOOL IMM_DestroyContext(HIMC hIMC) BOOL WINAPI ImmDestroyContext(HIMC hIMC) { if ((UINT_PTR)hIMC == NtUserGetThreadInfo()->default_imc) return FALSE; - if (!IMM_IsCrossThreadAccess(NULL, hIMC)) + if (!IMM_IsCrossThreadAccess(hIMC)) return IMM_DestroyContext(hIMC); else return FALSE; @@ -2244,7 +2239,7 @@ BOOL WINAPI ImmSetCandidateWindow( if (!data || !lpCandidate) return FALSE; - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; TRACE("\t%lx, %lx, %s, %s\n", @@ -2276,7 +2271,7 @@ BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) return FALSE; } - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; memcpy(&data->IMC.lfFont.W,lplf,sizeof(LOGFONTA)); @@ -2302,7 +2297,7 @@ BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) return FALSE; } - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; data->IMC.lfFont.W = *lplf; @@ -2333,7 +2328,7 @@ BOOL WINAPI ImmSetCompositionStringA( if (!data) return FALSE; - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; if (!(dwIndex == SCS_SETSTR || @@ -2390,7 +2385,7 @@ BOOL WINAPI ImmSetCompositionStringW( if (!data) return FALSE; - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; if (!(dwIndex == SCS_SETSTR || @@ -2451,7 +2446,7 @@ BOOL WINAPI ImmSetCompositionWindow( return FALSE; } - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; data->IMC.cfCompForm = *lpCompForm; @@ -2487,7 +2482,7 @@ BOOL WINAPI ImmSetConversionStatus( return FALSE; } - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; if ( fdwConversion != data->IMC.fdwConversion ) @@ -2523,7 +2518,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) return FALSE; } - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; if (data->ime->ui_hwnd == NULL) @@ -2560,7 +2555,7 @@ BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) return FALSE; } - if (IMM_IsCrossThreadAccess(NULL, hIMC)) + if (IMM_IsCrossThreadAccess(hIMC)) return FALSE; TRACE("\t%s\n", wine_dbgstr_point(lpptPos)); From 029ab287d476d015454583fab46dd079f902befa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 11 Mar 2023 12:11:21 +0100 Subject: [PATCH 142/758] imm32: Use NtUserQueryInputContext to check cross-thread access. (cherry picked from commit 1d40658afa139b94f540e32587bd1123853e0d1f) --- dlls/imm32/imm.c | 46 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 26ec7c00140..8c3f2b448ad 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -90,7 +90,6 @@ typedef struct tagInputContextData HIMC handle; DWORD dwLock; INPUTCONTEXT IMC; - DWORD threadID; struct ime *ime; UINT lastVK; @@ -682,17 +681,6 @@ static HIMCC ImmCreateBlankCompStr(void) return rc; } -static BOOL IMM_IsCrossThreadAccess(HIMC hIMC) -{ - InputContextData *data; - - data = get_imc_data(hIMC); - if (data && data->threadID != GetCurrentThreadId()) - return TRUE; - - return FALSE; -} - /*********************************************************************** * ImmSetActiveContext (IMM32.@) */ @@ -898,7 +886,6 @@ static InputContextData *create_input_context(HIMC default_imc) IMM_DestroyContext(new_context); return 0; } - new_context->threadID = GetCurrentThreadId(); SendMessageW( GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->ime ); TRACE("Created context %p\n", new_context); @@ -937,10 +924,8 @@ static BOOL IMM_DestroyContext(HIMC hIMC) BOOL WINAPI ImmDestroyContext(HIMC hIMC) { if ((UINT_PTR)hIMC == NtUserGetThreadInfo()->default_imc) return FALSE; - if (!IMM_IsCrossThreadAccess(hIMC)) - return IMM_DestroyContext(hIMC); - else - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + return IMM_DestroyContext(hIMC); } /*********************************************************************** @@ -2239,8 +2224,7 @@ BOOL WINAPI ImmSetCandidateWindow( if (!data || !lpCandidate) return FALSE; - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; TRACE("\t%lx, %lx, %s, %s\n", lpCandidate->dwIndex, lpCandidate->dwStyle, @@ -2271,8 +2255,7 @@ BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) return FALSE; } - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; memcpy(&data->IMC.lfFont.W,lplf,sizeof(LOGFONTA)); MultiByteToWideChar(CP_ACP, 0, lplf->lfFaceName, -1, data->IMC.lfFont.W.lfFaceName, @@ -2297,8 +2280,7 @@ BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) return FALSE; } - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; data->IMC.lfFont.W = *lplf; ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT); @@ -2328,8 +2310,7 @@ BOOL WINAPI ImmSetCompositionStringA( if (!data) return FALSE; - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; if (!(dwIndex == SCS_SETSTR || dwIndex == SCS_CHANGEATTR || @@ -2385,8 +2366,7 @@ BOOL WINAPI ImmSetCompositionStringW( if (!data) return FALSE; - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; if (!(dwIndex == SCS_SETSTR || dwIndex == SCS_CHANGEATTR || @@ -2446,8 +2426,7 @@ BOOL WINAPI ImmSetCompositionWindow( return FALSE; } - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; data->IMC.cfCompForm = *lpCompForm; @@ -2482,8 +2461,7 @@ BOOL WINAPI ImmSetConversionStatus( return FALSE; } - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; if ( fdwConversion != data->IMC.fdwConversion ) { @@ -2518,8 +2496,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) return FALSE; } - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; if (data->ime->ui_hwnd == NULL) { @@ -2555,8 +2532,7 @@ BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) return FALSE; } - if (IMM_IsCrossThreadAccess(hIMC)) - return FALSE; + if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; TRACE("\t%s\n", wine_dbgstr_point(lpptPos)); From ca9e9952c66b4e33fb68cc082eb7419c8750110c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 12:30:24 +0100 Subject: [PATCH 143/758] imm32: Rename InputContextData to struct imc. (cherry picked from commit 8f52d8a4e1f2c94e6613f77a6cd351db92b26ecd) --- dlls/imm32/imm.c | 114 +++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 8c3f2b448ad..c22c1fb9e64 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -85,7 +85,7 @@ struct ime static HRESULT (WINAPI *pCoRevokeInitializeSpy)(ULARGE_INTEGER cookie); static void (WINAPI *pCoUninitialize)(void); -typedef struct tagInputContextData +struct imc { HIMC handle; DWORD dwLock; @@ -93,7 +93,7 @@ typedef struct tagInputContextData struct ime *ime; UINT lastVK; -} InputContextData; +}; #define WINE_IMC_VALID_MAGIC 0x56434D49 @@ -129,7 +129,7 @@ static BOOL ime_is_unicode( const struct ime *ime ) } static BOOL IMM_DestroyContext(HIMC hIMC); -static InputContextData* get_imc_data(HIMC hIMC); +static struct imc *get_imc_data( HIMC hIMC ); static inline WCHAR *strdupAtoW( const char *str ) { @@ -402,9 +402,9 @@ static void imm_coinit_thread(void) InitOnceExecuteOnce(&init_ole32_once, init_ole32_funcs, NULL, NULL); } -static InputContextData *query_imc_data(HIMC handle) +static struct imc *query_imc_data( HIMC handle ) { - InputContextData *ret; + struct imc *ret; if (!handle) return NULL; ret = (void *)NtUserQueryInputContext(handle, NtUserInputContextClientPtr); @@ -566,7 +566,7 @@ static void ime_release( struct ime *ime ) static BOOL free_input_context_data( HIMC hIMC ) { - InputContextData *data = query_imc_data( hIMC ); + struct imc *data = query_imc_data( hIMC ); if (!data) return FALSE; @@ -637,7 +637,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) } /* for posting messages as the IME */ -static void ImmInternalPostIMEMessage(InputContextData *data, UINT msg, WPARAM wParam, LPARAM lParam) +static void ImmInternalPostIMEMessage( struct imc *data, UINT msg, WPARAM wParam, LPARAM lParam ) { HWND target = GetFocus(); if (!target) @@ -647,7 +647,7 @@ static void ImmInternalPostIMEMessage(InputContextData *data, UINT msg, WPARAM w } /* for sending messages as the IME */ -static void ImmInternalSendIMEMessage(InputContextData *data, UINT msg, WPARAM wParam, LPARAM lParam) +static void ImmInternalSendIMEMessage( struct imc *data, UINT msg, WPARAM wParam, LPARAM lParam ) { HWND target = GetFocus(); if (!target) @@ -656,7 +656,7 @@ static void ImmInternalSendIMEMessage(InputContextData *data, UINT msg, WPARAM w SendMessageW(target, msg, wParam, lParam); } -static LRESULT ImmInternalSendIMENotify(InputContextData *data, WPARAM notify, LPARAM lParam) +static LRESULT ImmInternalSendIMENotify( struct imc *data, WPARAM notify, LPARAM lParam ) { HWND target; @@ -686,7 +686,7 @@ static HIMCC ImmCreateBlankCompStr(void) */ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) { - InputContextData *data = get_imc_data(himc); + struct imc *data = get_imc_data( himc ); TRACE("(%p, %p, %x)\n", hwnd, himc, activate); @@ -830,14 +830,14 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) return ret; } -static InputContextData *create_input_context(HIMC default_imc) +static struct imc *create_input_context( HIMC default_imc ) { - InputContextData *new_context; + struct imc *new_context; LPGUIDELINE gl; LPCANDIDATEINFO ci; int i; - new_context = calloc( 1, sizeof(InputContextData) ); + new_context = calloc( 1, sizeof(*new_context) ); /* Load the IME */ if (!(new_context->ime = ime_acquire( GetKeyboardLayout( 0 ) ))) @@ -892,9 +892,9 @@ static InputContextData *create_input_context(HIMC default_imc) return new_context; } -static InputContextData* get_imc_data(HIMC handle) +static struct imc *get_imc_data( HIMC handle ) { - InputContextData *ret; + struct imc *ret; if ((ret = query_imc_data(handle)) || !handle) return ret; return create_input_context(handle); @@ -905,7 +905,7 @@ static InputContextData* get_imc_data(HIMC handle) */ HIMC WINAPI ImmCreateContext(void) { - InputContextData *new_context; + struct imc *new_context; if (!(new_context = create_input_context(0))) return 0; return new_context->handle; @@ -1067,7 +1067,7 @@ DWORD WINAPI ImmGetCandidateListA( HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; LPCANDIDATELIST candlist; DWORD ret = 0; @@ -1105,7 +1105,7 @@ DWORD WINAPI ImmGetCandidateListA( DWORD WINAPI ImmGetCandidateListCountA( HIMC hIMC, LPDWORD lpdwListCount) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; DWORD ret, count; @@ -1137,7 +1137,7 @@ DWORD WINAPI ImmGetCandidateListCountA( DWORD WINAPI ImmGetCandidateListCountW( HIMC hIMC, LPDWORD lpdwListCount) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; DWORD ret, count; @@ -1170,7 +1170,7 @@ DWORD WINAPI ImmGetCandidateListW( HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; LPCANDIDATELIST candlist; DWORD ret = 0; @@ -1208,7 +1208,7 @@ DWORD WINAPI ImmGetCandidateListW( BOOL WINAPI ImmGetCandidateWindow( HIMC hIMC, DWORD dwIndex, LPCANDIDATEFORM lpCandidate) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("%p, %ld, %p\n", hIMC, dwIndex, lpCandidate); @@ -1251,7 +1251,7 @@ BOOL WINAPI ImmGetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) */ BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p):\n", hIMC, lplf); @@ -1268,8 +1268,8 @@ BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) /* Source encoding is defined by context, source length is always given in respective characters. Destination buffer length is always in bytes. */ -static INT CopyCompStringIMEtoClient(const InputContextData *data, const void *src, INT src_len, void *dst, - INT dst_len, BOOL unicode) +static INT CopyCompStringIMEtoClient( const struct imc *data, const void *src, INT src_len, + void *dst, INT dst_len, BOOL unicode ) { int char_size = unicode ? sizeof(WCHAR) : sizeof(char); INT ret; @@ -1298,8 +1298,8 @@ static INT CopyCompStringIMEtoClient(const InputContextData *data, const void *s /* Composition string encoding is defined by context, returned attributes correspond to string, converted according to passed mode. String length is in characters, attributes are in byte arrays. */ -static INT CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src, INT src_len, const void *comp_string, - INT str_len, BYTE *dst, INT dst_len, BOOL unicode) +static INT CopyCompAttrIMEtoClient( const struct imc *data, const BYTE *src, INT src_len, const void *comp_string, + INT str_len, BYTE *dst, INT dst_len, BOOL unicode ) { union { @@ -1369,8 +1369,8 @@ static INT CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src return rc; } -static INT CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource, - LPBYTE target, INT tlen, BOOL unicode ) +static INT CopyCompClauseIMEtoClient( struct imc *data, LPBYTE source, INT slen, LPBYTE ssource, + LPBYTE target, INT tlen, BOOL unicode ) { INT rc; @@ -1424,7 +1424,7 @@ static INT CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT return rc; } -static INT CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYTE ssource, BOOL unicode) +static INT CopyCompOffsetIMEtoClient( struct imc *data, DWORD offset, LPBYTE ssource, BOOL unicode ) { int rc; @@ -1446,7 +1446,7 @@ static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen, BOOL unicode) { LONG rc = 0; - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); LPCOMPOSITIONSTRING compstr; LPBYTE compdata; @@ -1558,7 +1558,7 @@ LONG WINAPI ImmGetCompositionStringW( */ BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lpCompForm); @@ -1583,7 +1583,7 @@ HIMC WINAPI ImmGetContext(HWND hWnd) if (rc) { - InputContextData *data = get_imc_data(rc); + struct imc *data = get_imc_data( rc ); if (data) data->IMC.hWnd = hWnd; else rc = 0; } @@ -1671,7 +1671,7 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI BOOL WINAPI ImmGetConversionStatus( HIMC hIMC, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("%p %p %p\n", hIMC, lpfdwConversion, lpfdwSentence); @@ -1816,8 +1816,8 @@ UINT WINAPI ImmGetIMEFileNameW( HKL hkl, WCHAR *buffer, UINT length ) */ BOOL WINAPI ImmGetOpenStatus(HIMC hIMC) { - InputContextData *data = get_imc_data(hIMC); - static int i; + struct imc *data = get_imc_data( hIMC ); + static int i; if (!data) return FALSE; @@ -1915,7 +1915,7 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) */ BOOL WINAPI ImmGetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lpptPos); @@ -1933,7 +1933,7 @@ BOOL WINAPI ImmGetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) UINT WINAPI ImmGetVirtualKey(HWND hWnd) { OSVERSIONINFOA version; - InputContextData *data = get_imc_data( ImmGetContext( hWnd )); + struct imc *data = get_imc_data( ImmGetContext( hWnd ) ); TRACE("%p\n", hWnd); if ( data ) @@ -2096,7 +2096,7 @@ BOOL WINAPI ImmIsUIMessageW( BOOL WINAPI ImmNotifyIME( HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %ld, %ld, %ld)\n", hIMC, dwAction, dwIndex, dwValue); @@ -2186,7 +2186,7 @@ BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC) */ LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("%p %Id %Id\n", hIMC, wParam, wParam); @@ -2201,7 +2201,7 @@ LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam) */ LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("%p %Id %Id\n", hIMC, wParam, wParam); @@ -2217,7 +2217,7 @@ LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam) BOOL WINAPI ImmSetCandidateWindow( HIMC hIMC, LPCANDIDATEFORM lpCandidate) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lpCandidate); @@ -2246,7 +2246,7 @@ BOOL WINAPI ImmSetCandidateWindow( */ BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lplf); if (!data || !lplf) @@ -2271,7 +2271,7 @@ BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) */ BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lplf); if (!data || !lplf) @@ -2302,7 +2302,7 @@ BOOL WINAPI ImmSetCompositionStringA( WCHAR *CompBuffer = NULL; WCHAR *ReadBuffer = NULL; BOOL rc; - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); @@ -2358,7 +2358,7 @@ BOOL WINAPI ImmSetCompositionStringW( CHAR *CompBuffer = NULL; CHAR *ReadBuffer = NULL; BOOL rc; - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); @@ -2412,7 +2412,7 @@ BOOL WINAPI ImmSetCompositionWindow( HIMC hIMC, LPCOMPOSITIONFORM lpCompForm) { BOOL reshow = FALSE; - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lpCompForm); if (lpCompForm) @@ -2451,7 +2451,7 @@ BOOL WINAPI ImmSetConversionStatus( HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence) { DWORD oldConversion, oldSentence; - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("%p %ld %ld\n", hIMC, fdwConversion, fdwSentence); @@ -2486,7 +2486,7 @@ BOOL WINAPI ImmSetConversionStatus( */ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("%p %d\n", hIMC, fOpen); @@ -2522,7 +2522,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) */ BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); TRACE("(%p, %p)\n", hIMC, lpptPos); @@ -2641,7 +2641,7 @@ BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, con DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOA *parentA, IMEMENUITEMINFOA *menuA, DWORD size ) { - InputContextData *data = get_imc_data( himc ); + struct imc *data = get_imc_data( himc ); DWORD ret; TRACE( "himc %p, flags %#lx, type %lu, parentA %p, menuA %p, size %lu.\n", @@ -2699,7 +2699,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parentW, IMEMENUITEMINFOW *menuW, DWORD size ) { - InputContextData *data = get_imc_data( himc ); + struct imc *data = get_imc_data( himc ); DWORD ret; TRACE( "himc %p, flags %#lx, type %lu, parentW %p, menuW %p, size %lu.\n", @@ -2754,7 +2754,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE */ LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); if (!data) return NULL; @@ -2767,7 +2767,7 @@ LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC) */ BOOL WINAPI ImmUnlockIMC(HIMC hIMC) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); if (!data) return FALSE; @@ -2781,7 +2781,7 @@ BOOL WINAPI ImmUnlockIMC(HIMC hIMC) */ DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); if (!data) return 0; return data->dwLock; @@ -2848,7 +2848,7 @@ DWORD WINAPI ImmGetIMCCSize(HIMCC imcc) */ BOOL WINAPI ImmGenerateMessage(HIMC hIMC) { - InputContextData *data = get_imc_data(hIMC); + struct imc *data = get_imc_data( hIMC ); if (!data) { @@ -2888,7 +2888,7 @@ BOOL WINAPI ImmGenerateMessage(HIMC hIMC) */ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData) { - InputContextData *data; + struct imc *data; HIMC imc = ImmGetContext(hwnd); BYTE state[256]; UINT scancode; @@ -2947,7 +2947,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD */ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD unknown) { - InputContextData *data; + struct imc *data; HIMC imc = ImmGetContext(hwnd); BYTE state[256]; From 51654e3cab74cbe0856d99a45e44365d0eadcbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 23 Mar 2023 12:03:51 +0100 Subject: [PATCH 144/758] imm32: Fix ImmEscape(A|W) with NULL data. (cherry picked from commit 8f6eda8649b6b214181aa604361b29867372706a) --- dlls/imm32/imm.c | 4 ++-- dlls/imm32/tests/imm32.c | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index c22c1fb9e64..f202c4cf1e7 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1006,7 +1006,7 @@ LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data ) if (!(ime = ime_acquire( hkl ))) return 0; - if (!EscapeRequiresWA( code ) || !ime_is_unicode( ime )) + if (!EscapeRequiresWA( code ) || !ime_is_unicode( ime ) || !data) ret = ime->pImeEscape( himc, code, data ); else { @@ -1039,7 +1039,7 @@ LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data ) if (!(ime = ime_acquire( hkl ))) return 0; - if (!EscapeRequiresWA( code ) || ime_is_unicode( ime )) + if (!EscapeRequiresWA( code ) || ime_is_unicode( ime ) || !data) ret = ime->pImeEscape( himc, code, data ); else { diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 73fbe9d1aa5..a13adf213d5 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2566,15 +2566,9 @@ static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data ) case IME_ESC_SET_EUDC_DICTIONARY: if (!data) return 4; if (ime_info.fdwProperty & IME_PROP_UNICODE) - { - todo_wine_if(*(WCHAR *)data != 'E') ok_wcs( L"EscapeIme", data ); - } else - { - todo_wine_if(*(char *)data != 'E') ok_str( "EscapeIme", data ); - } /* fallthrough */ case IME_ESC_QUERY_SUPPORT: case IME_ESC_SEQUENCE_TO_INTERNAL: From bbbc3e2c177f769169e6c461461f4e18b6f873a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 23 Mar 2023 12:11:51 +0100 Subject: [PATCH 145/758] imm32: Fix ImmEnumRegisterWord(A|W) callback conversion. (cherry picked from commit 66e715f409912f814a199cab9d4c481d1acc4464) --- dlls/imm32/imm.c | 40 ++++++++++++++++++++++++++++++++++++++-- dlls/imm32/tests/imm32.c | 4 ---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index f202c4cf1e7..e47e8aa4c44 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -928,6 +928,23 @@ BOOL WINAPI ImmDestroyContext(HIMC hIMC) return IMM_DestroyContext(hIMC); } +struct enum_register_word_params_WtoA +{ + REGISTERWORDENUMPROCA proc; + void *user; +}; + +static int CALLBACK enum_register_word_WtoA( const WCHAR *readingW, DWORD style, + const WCHAR *stringW, void *user ) +{ + char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); + struct enum_register_word_params_WtoA *params = user; + int ret = params->proc( readingA, style, stringA, params->user ); + free( readingA ); + free( stringA ); + return ret; +} + /*********************************************************************** * ImmEnumRegisterWordA (IMM32.@) */ @@ -946,8 +963,9 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user ); else { + struct enum_register_word_params_WtoA params = {.proc = procA, .user = user}; WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); - ret = ime->pImeEnumRegisterWord( procA, readingW, style, stringW, user ); + ret = ime->pImeEnumRegisterWord( enum_register_word_WtoA, readingW, style, stringW, ¶ms ); free( readingW ); free( stringW ); } @@ -956,6 +974,23 @@ UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const ch return ret; } +struct enum_register_word_params_AtoW +{ + REGISTERWORDENUMPROCW proc; + void *user; +}; + +static int CALLBACK enum_register_word_AtoW( const char *readingA, DWORD style, + const char *stringA, void *user ) +{ + WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA ); + struct enum_register_word_params_AtoW *params = user; + int ret = params->proc( readingW, style, stringW, params->user ); + free( readingW ); + free( stringW ); + return ret; +} + /*********************************************************************** * ImmEnumRegisterWordW (IMM32.@) */ @@ -974,8 +1009,9 @@ UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WC ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user ); else { + struct enum_register_word_params_AtoW params = {.proc = procW, .user = user}; char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW ); - ret = ime->pImeEnumRegisterWord( procW, readingA, style, stringA, user ); + ret = ime->pImeEnumRegisterWord( enum_register_word_AtoW, readingA, style, stringA, ¶ms ); free( readingA ); free( stringA ); } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index a13adf213d5..317abb44870 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3400,9 +3400,7 @@ static int CALLBACK enum_register_wordA( const char *reading, DWORD style, const ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_a(reading), style, debugstr_a(string), user ); ok_eq( 0xdeadbeef, style, UINT, "%#x" ); - todo_wine_if( reading[1] == 0 ) ok_str( "Reading", reading ); - todo_wine_if( string[1] == 0 ) ok_str( "String", string ); return 0xdeadbeef; @@ -3413,9 +3411,7 @@ static int CALLBACK enum_register_wordW( const WCHAR *reading, DWORD style, cons ime_trace( "reading %s, style %#lx, string %s, user %p\n", debugstr_w(reading), style, debugstr_w(string), user ); ok_eq( 0xdeadbeef, style, UINT, "%#x" ); - todo_wine_if( reading[0] != 'R' ) ok_wcs( L"Reading", reading ); - todo_wine_if( string[0] != 'S' ) ok_wcs( L"String", string ); return 0xdeadbeef; From 403d133b4748195bd7f32fb142b0eb29d1b2ad6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 23 Mar 2023 12:18:25 +0100 Subject: [PATCH 146/758] imm32/tests: Reduce test output unnecessary verbosity. (cherry picked from commit b48a65930811f8e84e7d35f98377960892246928) --- dlls/imm32/tests/imm32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 317abb44870..6f29155dc62 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2841,7 +2841,7 @@ static HKL ime_install(void) ok( !wcscmp( buffer, L"WineTest IME" ), "got Layout Text %s\n", debugstr_w(buffer) ); len = sizeof(buffer); - memset( buffer, 0xcd, sizeof(buffer) ); + memset( buffer, 0, sizeof(buffer) ); ret = RegQueryValueExW( hkey, L"Layout File", NULL, NULL, (BYTE *)buffer, &len ); todo_wine ok( !ret, "RegQueryValueExW returned %#lx, error %lu\n", ret, GetLastError() ); From 85071a0731ed9a3ea142400eaccc8c22ed0c16f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 10:26:10 +0100 Subject: [PATCH 147/758] imm32: Stub ImmActivateLayout. (cherry picked from commit bcc2337f303f1c71bb3c4ed1afad174a73bfa0e3) --- dlls/imm32/imm.c | 6 ++++++ dlls/imm32/imm32.spec | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e47e8aa4c44..1b8caf90c1c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -564,6 +564,12 @@ static void ime_release( struct ime *ime ) LeaveCriticalSection( &ime_cs ); } +BOOL WINAPI ImmActivateLayout( HKL hkl ) +{ + FIXME( "hkl %p stub!\n", hkl ); + return FALSE; +} + static BOOL free_input_context_data( HIMC hIMC ) { struct imc *data = query_imc_data( hIMC ); diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 9c7ce13319f..47b3916c822 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -1,4 +1,4 @@ -@ stub ImmActivateLayout +@ stdcall ImmActivateLayout(long) @ stdcall ImmAssociateContext(long long) @ stdcall ImmAssociateContextEx(long long long) @ stdcall ImmConfigureIMEA(long long long ptr) From 174dbf1e98bbeeac4273f4b541601aee08e5490f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 10:35:09 +0100 Subject: [PATCH 148/758] imm32/tests: Test undocumented ImmActivateLayout. (cherry picked from commit 7b3e66678ab3fbb54504b4430de9393c99659b22) --- dlls/imm32/tests/imm32.c | 183 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 6f29155dc62..d22b384c7a5 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -84,6 +84,7 @@ static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t); extern BOOL WINAPI ImmFreeLayout(HKL); extern BOOL WINAPI ImmLoadIME(HKL); +extern BOOL WINAPI ImmActivateLayout(HKL); #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE @@ -2481,6 +2482,104 @@ DEFINE_EXPECT( IME_DLL_PROCESS_DETACH ); static IMEINFO ime_info; +enum ime_function +{ + IME_SELECT = 1, + IME_NOTIFY, +}; + +struct ime_call +{ + enum ime_function func; + HIMC himc; + + union + { + int select; + struct + { + int action; + int index; + int value; + } notify; + }; + + BOOL todo; +}; + +struct ime_call empty_sequence[] = {{0}}; +static struct ime_call ime_calls[1024]; +static ULONG ime_call_count; + +#define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b ) +static void ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received ) +{ + int ret; + + if ((ret = expected->func - received->func)) goto done; + if ((ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done; + switch (expected->func) + { + case IME_SELECT: + if ((ret = expected->select - received->select)) goto done; + break; + case IME_NOTIFY: + if ((ret = expected->notify.action - received->notify.action)) goto done; + if ((ret = expected->notify.index - received->notify.index)) goto done; + if ((ret = expected->notify.value - received->notify.value)) goto done; + break; + } + +done: + switch (received->func) + { + case IME_SELECT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got IME_SELECT himc %p, select %u\n", received->himc, received->select ); + return; + case IME_NOTIFY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got IME_NOTIFY himc %p, action %#x, index %#x, value %#x\n", + received->himc, received->notify.action, received->notify.index, + received->notify.value ); + return; + } + + switch (expected->func) + { + case IME_SELECT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "IME_SELECT himc %p, select %u\n", expected->himc, expected->select ); + break; + case IME_NOTIFY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "IME_NOTIFY himc %p, action %#x, index %#x, value %#x\n", + expected->himc, expected->notify.action, expected->notify.index, + expected->notify.value ); + break; + } +} + +#define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a ) +static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context ) +{ + const struct ime_call *received = ime_calls; + UINT i = 0; + + while (expected->func || received->func) + { + winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "", + !received->func ? " (missing)" : "" ); + ok_call_( file, line, expected, received ); + if (expected->func) expected++; + if (received->func) received++; + winetest_pop_context(); + } + + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); @@ -2675,9 +2774,10 @@ static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select ) { + struct ime_call call = {.func = IME_SELECT, .himc = himc, .select = select}; ime_trace( "himc %p, select %d\n", himc, select ); - ok( 0, "unexpected call\n" ); - return FALSE; + ime_calls[ime_call_count++] = call; + return TRUE; } static BOOL WINAPI ime_ImeSetActiveContext( HIMC himc, BOOL flag ) @@ -2727,8 +2827,9 @@ static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, con static BOOL WINAPI ime_NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) { - ime_trace( "himc %p, action %lu, index %lu, value %lu\n", himc, action, index, value ); - ok( 0, "unexpected call\n" ); + struct ime_call call = {.func = IME_NOTIFY, .himc = himc, .notify = {.action = action, .index = index, .value = value}}; + ime_trace( "himc %p, action %#lx, index %lu, value %lu\n", himc, action, index, value ); + ime_calls[ime_call_count++] = call; return FALSE; } @@ -3664,6 +3765,78 @@ static void test_ImmUnregisterWord( BOOL unicode ) winetest_pop_context(); } +static void test_ImmActivateLayout(void) +{ + const struct ime_call activate_seq[] = + { + {.func = IME_SELECT, .himc = default_himc, .select = 1, .todo = TRUE}, + {0}, + }; + const struct ime_call deactivate_seq[] = + { + {.func = IME_NOTIFY, .himc = default_himc, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, .todo = TRUE}, + {.func = IME_SELECT, .himc = default_himc, .select = 0, .todo = TRUE}, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + UINT ret; + + SET_ENABLE( ImeInquire, TRUE ); + SET_ENABLE( ImeDestroy, TRUE ); + + todo_wine + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + /* ActivateKeyboardLayout doesn't call ImeInquire / ImeDestroy */ + + ok_seq( empty_sequence ); + ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + ok_seq( empty_sequence ); + ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + + /* ImmActivateLayout changes active HKL */ + + SET_EXPECT( ImeInquire ); + todo_wine + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_seq( activate_seq ); + todo_wine + CHECK_CALLED( ImeInquire ); + + todo_wine + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + todo_wine + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_seq( deactivate_seq ); + + ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ime_cleanup( hkl ); + ok_seq( empty_sequence ); + + + /* ImmActivateLayout leaks the IME, we need to free it manually */ + + SET_EXPECT( ImeDestroy ); + ret = ImmFreeLayout( hkl ); + ok( ret, "ImmFreeLayout returned %u\n", ret ); + todo_wine + CHECK_CALLED( ImeDestroy ); + ok_seq( empty_sequence ); + +cleanup: + SET_ENABLE( ImeInquire, FALSE ); + SET_ENABLE( ImeDestroy, FALSE ); +} + START_TEST(imm32) { if (!is_ime_enabled()) @@ -3693,6 +3866,8 @@ START_TEST(imm32) test_ImmUnregisterWord( FALSE ); test_ImmUnregisterWord( TRUE ); + test_ImmActivateLayout(); + if (init()) { test_ImmNotifyIME(); From 1ecb415eefbbfa7e1788192a5bd621446315a955 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Tue, 21 Mar 2023 04:18:26 +0100 Subject: [PATCH 149/758] imm32/tests: Document the WINE_LANGID value. (cherry picked from commit 683ad8221b54ff37f5c185b0571da893bf409792) --- dlls/imm32/tests/ime_wrapper.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/imm32/tests/ime_wrapper.rc b/dlls/imm32/tests/ime_wrapper.rc index 812754ce4ce..e71d81f4fc9 100644 --- a/dlls/imm32/tests/ime_wrapper.rc +++ b/dlls/imm32/tests/ime_wrapper.rc @@ -18,7 +18,7 @@ #pragma makedep testdll -#define WINE_LANGID 047f +#define WINE_LANGID 047f /* LANG_INVARIANT/SUBLANG_DEFAULT */ #define WINE_FILETYPE VFT_DRV #define WINE_FILESUBTYPE VFT2_DRV_INPUTMETHOD #define WINE_FILENAME "ime_wrapper" From 186662f0a44482bea561bd82b17d1e1c06c5891a Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Mar 2023 13:44:57 +0100 Subject: [PATCH 150/758] win32u: Fix printf format warnings. (cherry picked from commit d0246c3ea0680f0bb40dcd0e5b54c942e096925c) --- dlls/win32u/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index c7c8932e385..15eebd4a30c 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2501,7 +2501,7 @@ BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_P UINT32 *entry_count, UINT32 *pointer_count, void *pointer_info ) { FIXME( "id %#x, type %#x, unk0 %#zx, unk1 %#zx, size %#zx, entry_count %p, pointer_count %p, pointer_info %p stub!\n", - id, (int)type, unk0, unk1, (size_t)size, entry_count, pointer_count, pointer_info ); + id, (int)type, (size_t)unk0, (size_t)unk1, (size_t)size, entry_count, pointer_count, pointer_info ); RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED ); return FALSE; } From c91bc4bf15f8e2cebfaea755eb6eabbf76d3db62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 14:45:00 +0200 Subject: [PATCH 151/758] win32u: Allow LANG_INVARIANT in NtUserActivateKeyboardLayout. (cherry picked from commit 4571456e703b944cdb7bc193ddb26da63635ff6e) --- dlls/win32u/input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 15eebd4a30c..a983ac0b706 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1209,7 +1209,8 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) return 0; } - if (NtQueryDefaultLocale( TRUE, &locale ) || LOWORD(layout) != locale) + if (LOWORD(layout) != MAKELANGID(LANG_INVARIANT, SUBLANG_DEFAULT) && + (NtQueryDefaultLocale( TRUE, &locale ) || LOWORD(layout) != locale)) { RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED ); FIXME_(keyboard)( "Changing user locale is not supported\n" ); From b693e94804f37f611b55009121adc19815ac3575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 14:45:42 +0200 Subject: [PATCH 152/758] imm32: Use installed IME language for the created HKL. (cherry picked from commit db5cf9a5e12b6e8f664f54a626f4cf6efc2bfbf3) --- dlls/imm32/Makefile.in | 2 +- dlls/imm32/imm.c | 26 ++++++++++++++++++++++---- dlls/imm32/tests/imm32.c | 1 - 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/Makefile.in b/dlls/imm32/Makefile.in index 29de6063792..b4e3039849e 100644 --- a/dlls/imm32/Makefile.in +++ b/dlls/imm32/Makefile.in @@ -1,6 +1,6 @@ MODULE = imm32.dll IMPORTLIB = imm32 -IMPORTS = user32 gdi32 advapi32 win32u +IMPORTS = user32 gdi32 advapi32 kernelbase win32u DELAYIMPORTS = ole32 C_SRCS = \ diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 1b8caf90c1c..e600156f504 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2012,13 +2012,31 @@ HKL WINAPI ImmInstallIMEA( const char *filenameA, const char *descriptionA ) return hkl; } +static LCID get_ime_file_lang( const WCHAR *filename ) +{ + DWORD *languages; + LCID lcid = 0; + void *info; + UINT len; + + if (!(len = GetFileVersionInfoSizeW( filename, NULL ))) return 0; + if (!(info = malloc( len ))) goto done; + if (!GetFileVersionInfoW( filename, 0, len, info )) goto done; + if (!VerQueryValueW( info, L"\\VarFileInfo\\Translation", (void **)&languages, &len ) || !len) goto done; + lcid = languages[0]; + +done: + free( info ); + return lcid; +} + /*********************************************************************** * ImmInstallIMEW (IMM32.@) */ HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description ) { WCHAR path[ARRAY_SIZE(layouts_formatW)+8], buffer[MAX_PATH]; - LCID lcid = GetUserDefaultLCID(); + LCID lcid; WORD count = 0x20; const WCHAR *tmp; DWORD length; @@ -2027,7 +2045,7 @@ HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description ) TRACE( "filename %s, description %s\n", debugstr_w(filename), debugstr_w(description) ); - if (!filename || !description) + if (!filename || !description || !(lcid = get_ime_file_lang( filename ))) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; @@ -2037,8 +2055,8 @@ HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description ) { DWORD disposition = 0; - hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count ); - swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG_PTR)hkl); + hkl = (HKL)(UINT_PTR)MAKELONG( lcid, 0xe000 | count ); + swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG)(ULONG_PTR)hkl); if (!RegCreateKeyExW( HKEY_LOCAL_MACHINE, path, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition )) { diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index d22b384c7a5..7b9abb0b079 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2922,7 +2922,6 @@ static HKL ime_install(void) "MoveFileW failed, error %lu\n", GetLastError() ); hkl = ImmInstallIMEW( ime_path, L"WineTest IME" ); - todo_wine ok( hkl == (HKL)(int)0xe020047f, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() ); swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl ); From bc44ec51dd75e707b8d3fa2232551d4486f12bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 14:50:20 +0200 Subject: [PATCH 153/758] imm32/tests: Check current keyboard layout during ime calls. (cherry picked from commit ca97db75d12d4853285f94cd151759bbd845ef1f) --- dlls/imm32/tests/imm32.c | 65 +++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 7b9abb0b079..bfc61e05346 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2481,6 +2481,11 @@ static BOOL todo_IME_DLL_PROCESS_DETACH; DEFINE_EXPECT( IME_DLL_PROCESS_DETACH ); static IMEINFO ime_info; +static UINT ime_count; +static WCHAR ime_path[MAX_PATH]; +static HIMC default_himc; +static HKL default_hkl; +static HKL expect_ime = (HKL)(int)0xe020047f; enum ime_function { @@ -2490,8 +2495,9 @@ enum ime_function struct ime_call { - enum ime_function func; + HKL hkl; HIMC himc; + enum ime_function func; union { @@ -2518,6 +2524,7 @@ static void ok_call_( const char *file, int line, const struct ime_call *expecte if ((ret = expected->func - received->func)) goto done; if ((ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done; + if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done; switch (expected->func) { case IME_SELECT: @@ -2535,12 +2542,12 @@ static void ok_call_( const char *file, int line, const struct ime_call *expecte { case IME_SELECT: todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got IME_SELECT himc %p, select %u\n", received->himc, received->select ); + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select ); return; case IME_NOTIFY: todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got IME_NOTIFY himc %p, action %#x, index %#x, value %#x\n", - received->himc, received->notify.action, received->notify.index, + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", + received->hkl, received->himc, received->notify.action, received->notify.index, received->notify.value ); return; } @@ -2549,12 +2556,12 @@ static void ok_call_( const char *file, int line, const struct ime_call *expecte { case IME_SELECT: todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "IME_SELECT himc %p, select %u\n", expected->himc, expected->select ); + ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select ); break; case IME_NOTIFY: todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "IME_NOTIFY himc %p, action %#x, index %#x, value %#x\n", - expected->himc, expected->notify.action, expected->notify.index, + ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", + expected->hkl, expected->himc, expected->notify.action, expected->notify.index, expected->notify.value ); break; } @@ -2630,6 +2637,7 @@ static UINT WINAPI ime_ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WC ime_trace( "proc %p, reading %s, style %lu, string %s, data %p\n", proc, debugstr_w(reading), style, debugstr_w(string), data ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); CHECK_EXPECT( ImeEnumRegisterWord ); if (!style) @@ -2658,6 +2666,7 @@ static LRESULT WINAPI ime_ImeEscape( HIMC himc, UINT escape, void *data ) { ime_trace( "himc %p, escape %#x, data %p\n", himc, escape, data ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); CHECK_EXPECT( ImeEscape ); switch (escape) @@ -2701,6 +2710,7 @@ static UINT WINAPI ime_ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style ) { ime_trace( "item %u, style %p\n", item, style ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); CHECK_EXPECT( ImeGetRegisterWordStyle ); if (!style) @@ -2747,6 +2757,7 @@ static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYT { ime_trace( "himc %p, vkey %u, key_data %#Ix, key_state %p\n", himc, vkey, key_data, key_state ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); ok( 0, "unexpected call\n" ); return FALSE; } @@ -2755,6 +2766,7 @@ static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const { ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); CHECK_EXPECT( ImeRegisterWord ); if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" ); @@ -2774,7 +2786,11 @@ static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select ) { - struct ime_call call = {.func = IME_SELECT, .himc = himc, .select = select}; + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = himc, + .func = IME_SELECT, .select = select + }; ime_trace( "himc %p, select %d\n", himc, select ); ime_calls[ime_call_count++] = call; return TRUE; @@ -2808,6 +2824,7 @@ static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, con { ime_trace( "reading %s, style %lu, string %s\n", debugstr_w(reading), style, debugstr_w(string) ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); CHECK_EXPECT( ImeUnregisterWord ); if (style) ok_eq( 0xdeadbeef, style, UINT, "%#x" ); @@ -2827,7 +2844,11 @@ static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, con static BOOL WINAPI ime_NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) { - struct ime_call call = {.func = IME_NOTIFY, .himc = himc, .notify = {.action = action, .index = index, .value = value}}; + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = himc, + .func = IME_NOTIFY, .notify = {.action = action, .index = index, .value = value} + }; ime_trace( "himc %p, action %#lx, index %lu, value %lu\n", himc, action, index, value ); ime_calls[ime_call_count++] = call; return FALSE; @@ -2878,10 +2899,6 @@ static struct ime_functions ime_functions = ime_DllMain, }; -static UINT ime_count; -static WCHAR ime_path[MAX_PATH]; -static HIMC default_himc; - static HKL ime_install(void) { WCHAR buffer[MAX_PATH]; @@ -2922,7 +2939,7 @@ static HKL ime_install(void) "MoveFileW failed, error %lu\n", GetLastError() ); hkl = ImmInstallIMEW( ime_path, L"WineTest IME" ); - ok( hkl == (HKL)(int)0xe020047f, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() ); + ok( hkl == expect_ime, "ImmInstallIMEW returned %p, error %lu\n", hkl, GetLastError() ); swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl ); ret = RegOpenKeyW( HKEY_LOCAL_MACHINE, buffer, &hkey ); @@ -3768,13 +3785,25 @@ static void test_ImmActivateLayout(void) { const struct ime_call activate_seq[] = { - {.func = IME_SELECT, .himc = default_himc, .select = 1, .todo = TRUE}, + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_SELECT, .select = 1, + .todo = TRUE, + }, {0}, }; const struct ime_call deactivate_seq[] = { - {.func = IME_NOTIFY, .himc = default_himc, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, .todo = TRUE}, - {.func = IME_SELECT, .himc = default_himc, .select = 0, .todo = TRUE}, + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, + .todo = TRUE, + }, + { + .hkl = default_hkl, .himc = default_himc, + .func = IME_SELECT, .select = 0, + .todo = TRUE, + }, {0}, }; HKL hkl, old_hkl = GetKeyboardLayout( 0 ); @@ -3838,6 +3867,8 @@ static void test_ImmActivateLayout(void) START_TEST(imm32) { + default_hkl = GetKeyboardLayout( 0 ); + if (!is_ime_enabled()) { win_skip("IME support not implemented\n"); From 9bc03d3a3bb2e1e332ef882214c9eb4de2a80228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 10:33:45 +0200 Subject: [PATCH 154/758] imm32: Call ActivateKeyboardLayout from ImmActivateLayout. (cherry picked from commit 47533974ce156c90b203fe59e69a17b17ed312b6) --- dlls/imm32/imm.c | 8 ++++++-- dlls/imm32/tests/imm32.c | 7 +++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e600156f504..ee9e673233b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -566,8 +566,12 @@ static void ime_release( struct ime *ime ) BOOL WINAPI ImmActivateLayout( HKL hkl ) { - FIXME( "hkl %p stub!\n", hkl ); - return FALSE; + FIXME( "hkl %p semi-stub!\n", hkl ); + + if (hkl == GetKeyboardLayout( 0 )) return TRUE; + if (!ActivateKeyboardLayout( hkl, 0 )) return FALSE; + + return TRUE; } static BOOL free_input_context_data( HIMC hIMC ) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index bfc61e05346..0dfe3cddf90 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3812,7 +3812,6 @@ static void test_ImmActivateLayout(void) SET_ENABLE( ImeInquire, TRUE ); SET_ENABLE( ImeDestroy, TRUE ); - todo_wine ok_ret( 1, ImmActivateLayout( old_hkl ) ); ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -3832,16 +3831,16 @@ static void test_ImmActivateLayout(void) /* ImmActivateLayout changes active HKL */ SET_EXPECT( ImeInquire ); - todo_wine ok_ret( 1, ImmActivateLayout( hkl ) ); ok_seq( activate_seq ); todo_wine CHECK_CALLED( ImeInquire ); - todo_wine ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - todo_wine + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_seq( empty_sequence ); + ok_ret( 1, ImmActivateLayout( old_hkl ) ); ok_seq( deactivate_seq ); From 327d508ee2d6c20f5a16ee53c4fe20571b61b3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 15:23:11 +0200 Subject: [PATCH 155/758] imm32/tests: Test ImmCreateInputContext et al. (cherry picked from commit c2c27e7927a573199dbfc927d6caae2e57676ace) --- dlls/imm32/tests/imm32.c | 161 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 0dfe3cddf90..392f14cdcf3 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2511,6 +2511,7 @@ struct ime_call }; BOOL todo; + BOOL flaky_himc; }; struct ime_call empty_sequence[] = {{0}}; @@ -2523,7 +2524,9 @@ static void ok_call_( const char *file, int line, const struct ime_call *expecte int ret; if ((ret = expected->func - received->func)) goto done; - if ((ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done; + /* Wine doesn't allocate HIMC in a deterministic order, ignore them when they are enumerated */ + if (expected->flaky_himc && (ret = !!(UINT_PTR)expected->himc - !!(UINT_PTR)received->himc)) goto done; + if (!expected->flaky_himc && (ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done; if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done; switch (expected->func) { @@ -2590,7 +2593,6 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); - ok( 0, "unexpected call\n" ); return DefWindowProcW( hwnd, msg, wparam, lparam ); } @@ -3864,6 +3866,160 @@ static void test_ImmActivateLayout(void) SET_ENABLE( ImeDestroy, FALSE ); } +static void test_ImmCreateInputContext(void) +{ + struct ime_call activate_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_SELECT, .select = 1, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = expect_ime, .himc = 0/*himc[0]*/, + .func = IME_SELECT, .select = 1, + .todo = TRUE, .flaky_himc = TRUE, + }, + {0}, + }; + struct ime_call select1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc[1]*/, + .func = IME_SELECT, .select = 1, + .todo = TRUE, + }, + {0}, + }; + struct ime_call select0_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc[1]*/, + .func = IME_SELECT, .select = 0, + }, + {0}, + }; + struct ime_call deactivate_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = expect_ime, .himc = 0/*himc[0]*/, + .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = default_hkl, .himc = default_himc, + .func = IME_SELECT, .select = 0, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = default_hkl, .himc = 0/*himc[0]*/, + .func = IME_SELECT, .select = 0, + .todo = TRUE, .flaky_himc = TRUE, + }, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + INPUTCONTEXT *ctx; + HIMC himc[2]; + HWND hwnd; + + ctx = ImmLockIMC( default_himc ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ok_ret( 0, IsWindow( ctx->hWnd ) ); + ok_ret( 1, ImmUnlockIMC( default_himc ) ); + + + /* new input contexts cannot be locked before IME window has been created */ + + himc[0] = ImmCreateContext(); + ok( !!himc[0], "ImmCreateContext failed, error %lu\n", GetLastError() ); + ctx = ImmLockIMC( himc[0] ); + todo_wine + ok( !ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + if (ctx) ImmUnlockIMCC( himc[0] ); + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ctx = ImmLockIMC( default_himc ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ok_ret( 1, ImmUnlockIMC( default_himc ) ); + + ctx = ImmLockIMC( himc[0] ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ok_ret( 1, ImmUnlockIMC( himc[0] ) ); + + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + ime_info.dwPrivateDataSize = 123; + + if (!(hkl = ime_install())) goto cleanup; + + + /* Activating the layout calls ImeSelect 1 on existing HIMC */ + + ok_seq( empty_sequence ); + ok_ret( 1, ImmActivateLayout( hkl ) ); + activate_seq[1].himc = himc[0]; + ok_seq( activate_seq ); + + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ctx = ImmLockIMC( default_himc ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ok_ret( 1, ImmUnlockIMC( default_himc ) ); + + ctx = ImmLockIMC( himc[0] ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ok_ret( 1, ImmUnlockIMC( himc[0] ) ); + + + /* ImmLockIMC triggers the ImeSelect call, to allocate private data */ + + himc[1] = ImmCreateContext(); + ok( !!himc[1], "ImmCreateContext failed, error %lu\n", GetLastError() ); + + todo_wine + ok_seq( empty_sequence ); + ctx = ImmLockIMC( himc[1] ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + select1_seq[0].himc = himc[1]; + ok_seq( select1_seq ); + + ok_ret( 1, ImmUnlockIMC( himc[1] ) ); + + ok_seq( empty_sequence ); + ok_ret( 1, ImmDestroyContext( himc[1] ) ); + select0_seq[0].himc = himc[1]; + ok_seq( select0_seq ); + + + /* Deactivating the layout calls ImeSelect 0 on existing HIMC */ + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + deactivate_seq[1].himc = himc[0]; + deactivate_seq[3].himc = himc[0]; + ok_seq( deactivate_seq ); + + ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + ime_cleanup( hkl ); + ok_seq( empty_sequence ); + +cleanup: + ok_ret( 1, ImmDestroyContext( himc[0] ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + + ime_info.dwPrivateDataSize = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -3896,6 +4052,7 @@ START_TEST(imm32) test_ImmUnregisterWord( TRUE ); test_ImmActivateLayout(); + test_ImmCreateInputContext(); if (init()) { From caec1ea90a2a8f9f1fec1a250b01dd7885087cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 25 Mar 2023 13:47:20 +0100 Subject: [PATCH 156/758] imm32/tests: Test ActivateKeyboardLayout with an existing window. (cherry picked from commit 462d5ca257db7ac6a8a7f6dcee0344d6ea99547b) --- dlls/imm32/tests/imm32.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 392f14cdcf3..5dde6cd1b1f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3861,6 +3861,44 @@ static void test_ImmActivateLayout(void) CHECK_CALLED( ImeDestroy ); ok_seq( empty_sequence ); + + /* when there's a window, ActivateKeyboardLayout calls ImeInquire */ + + if (!(hkl = ime_install())) goto cleanup; + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + ok_seq( empty_sequence ); + + SET_EXPECT( ImeInquire ); + ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + todo_wine + CHECK_CALLED( ImeInquire ); + ok_seq( activate_seq ); + + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + ok_seq( deactivate_seq ); + + ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ime_cleanup( hkl ); + ok_seq( empty_sequence ); + + /* ImmActivateLayout leaks the IME, we need to free it manually */ + + SET_EXPECT( ImeDestroy ); + ret = ImmFreeLayout( hkl ); + ok( ret, "ImmFreeLayout returned %u\n", ret ); + todo_wine + CHECK_CALLED( ImeDestroy ); + ok_seq( empty_sequence ); + + ok_ret( 1, DestroyWindow( hwnd ) ); + + cleanup: SET_ENABLE( ImeInquire, FALSE ); SET_ENABLE( ImeDestroy, FALSE ); From 40b063bfb47d035f1f23f97963ada6c04983df8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 12:48:51 +0200 Subject: [PATCH 157/758] imm32: Enumerate input contexts in ImmActivateLayout. (cherry picked from commit 0aa3b85b9795f2e18e16589c60e4bb1ed418e108) --- dlls/imm32/imm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index ee9e673233b..803fb555786 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -564,13 +564,21 @@ static void ime_release( struct ime *ime ) LeaveCriticalSection( &ime_cs ); } +static BOOL CALLBACK enum_activate_layout( HIMC himc, LPARAM lparam ) +{ + if (ImmLockIMC( himc )) ImmUnlockIMC( himc ); + return TRUE; +} + BOOL WINAPI ImmActivateLayout( HKL hkl ) { - FIXME( "hkl %p semi-stub!\n", hkl ); + TRACE( "hkl %p\n", hkl ); if (hkl == GetKeyboardLayout( 0 )) return TRUE; if (!ActivateKeyboardLayout( hkl, 0 )) return FALSE; + ImmEnumInputContext( 0, enum_activate_layout, 0 ); + return TRUE; } From 81d54cb6ff565c49d35aa6c44cbd8c4d57897320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 12:49:06 +0200 Subject: [PATCH 158/758] imm32: Introduce a new imc_select_hkl helper. (cherry picked from commit 9c8b3e6f421fa1c5a0158f596b33bd3861c28cf7) --- dlls/imm32/imm.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 803fb555786..473f1927753 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -564,6 +564,23 @@ static void ime_release( struct ime *ime ) LeaveCriticalSection( &ime_cs ); } +static void imc_select_hkl( struct imc *imc, HKL hkl ) +{ + if (imc->ime) + { + if (imc->ime->hkl == hkl) return; + imc->ime->pImeSelect( imc->handle, FALSE ); + ime_release( imc->ime ); + } + + if (!hkl) + imc->ime = NULL; + else if (!(imc->ime = ime_acquire( hkl ))) + WARN( "Failed to acquire IME for HKL %p\n", hkl ); + else + imc->ime->pImeSelect( imc->handle, TRUE ); +} + static BOOL CALLBACK enum_activate_layout( HIMC himc, LPARAM lparam ) { if (ImmLockIMC( himc )) ImmUnlockIMC( himc ); @@ -3026,20 +3043,8 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD TRACE("%p %p %x %x %lx\n",hwnd, hKL, vKey, (UINT)lKeyData, unknown); if (!(data = get_imc_data( imc ))) return FALSE; - - /* Make sure we are inputting to the correct keyboard */ - if (data->ime->hkl != hKL) - { - struct ime *new_hkl; - - if (!(new_hkl = ime_acquire( hKL ))) return FALSE; - - data->ime->pImeSelect( imc, FALSE ); - ime_release( data->ime ); - - data->ime = new_hkl; - data->ime->pImeSelect( imc, TRUE ); - } + imc_select_hkl( data, hKL ); + if (!data->ime) return FALSE; GetKeyboardState(state); if (data->ime->pImeProcessKey( imc, vKey, lKeyData, state )) From ceb1c3ac27c237520a9322bc09341532ab9106e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 09:27:28 +0200 Subject: [PATCH 159/758] imm32: Update HIMC private data when selecting IME. (cherry picked from commit de4323611a232fd0cd25c59625ff08af91c75a8f) --- dlls/imm32/imm.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 473f1927753..04bfc461658 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -571,6 +571,7 @@ static void imc_select_hkl( struct imc *imc, HKL hkl ) if (imc->ime->hkl == hkl) return; imc->ime->pImeSelect( imc->handle, FALSE ); ime_release( imc->ime ); + ImmDestroyIMCC( imc->IMC.hPrivate ); } if (!hkl) @@ -578,7 +579,13 @@ static void imc_select_hkl( struct imc *imc, HKL hkl ) else if (!(imc->ime = ime_acquire( hkl ))) WARN( "Failed to acquire IME for HKL %p\n", hkl ); else + { + if (!(imc->IMC.hPrivate = ImmCreateIMCC( imc->ime->info.dwPrivateDataSize ))) + WARN( "Failed to allocate IME private data for IMC %p\n", imc ); + imc->IMC.fdwConversion = imc->ime->info.fdwConversionCaps; + imc->IMC.fdwSentence = imc->ime->info.fdwSentenceCaps; imc->ime->pImeSelect( imc->handle, TRUE ); + } } static BOOL CALLBACK enum_activate_layout( HIMC himc, LPARAM lparam ) @@ -872,15 +879,7 @@ static struct imc *create_input_context( HIMC default_imc ) LPCANDIDATEINFO ci; int i; - new_context = calloc( 1, sizeof(*new_context) ); - - /* Load the IME */ - if (!(new_context->ime = ime_acquire( GetKeyboardLayout( 0 ) ))) - { - TRACE("IME dll could not be loaded\n"); - free( new_context ); - return 0; - } + if (!(new_context = calloc( 1, sizeof(*new_context) ))) return NULL; /* the HIMCCs are never NULL */ new_context->IMC.hCompStr = ImmCreateBlankCompStr(); @@ -899,12 +898,6 @@ static struct imc *create_input_context( HIMC default_imc ) for (i = 0; i < ARRAY_SIZE(new_context->IMC.cfCandForm); i++) new_context->IMC.cfCandForm[i].dwIndex = ~0u; - /* Initialize the IME Private */ - new_context->IMC.hPrivate = ImmCreateIMCC( new_context->ime->info.dwPrivateDataSize ); - - new_context->IMC.fdwConversion = new_context->ime->info.fdwConversionCaps; - new_context->IMC.fdwSentence = new_context->ime->info.fdwSentenceCaps; - if (!default_imc) new_context->handle = NtUserCreateInputContext((UINT_PTR)new_context); else if (NtUserUpdateInputContext(default_imc, NtUserInputContextClientPtr, (UINT_PTR)new_context)) @@ -915,12 +908,7 @@ static struct imc *create_input_context( HIMC default_imc ) return 0; } - if (!new_context->ime->pImeSelect( new_context->handle, TRUE )) - { - TRACE("Selection of IME failed\n"); - IMM_DestroyContext(new_context); - return 0; - } + imc_select_hkl( new_context, GetKeyboardLayout( 0 ) ); SendMessageW( GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->ime ); TRACE("Created context %p\n", new_context); From 4e50f55e4fab8c7c8ebb6de25c194a22c5f37b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 09:27:28 +0200 Subject: [PATCH 160/758] imm32: Call ImeSelect from ImmLockIMC with current IME. (cherry picked from commit 61219f1b8be1f827b99dd2ffc5c5046b751d664c) --- dlls/imm32/imm.c | 15 +++++++++------ dlls/imm32/tests/imm32.c | 20 ++++++++++++++------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 04bfc461658..2c3af063ac5 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2829,14 +2829,17 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE /*********************************************************************** * ImmLockIMC(IMM32.@) */ -LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC) +INPUTCONTEXT *WINAPI ImmLockIMC( HIMC himc ) { - struct imc *data = get_imc_data( hIMC ); + struct imc *imc = get_imc_data( himc ); - if (!data) - return NULL; - data->dwLock++; - return &data->IMC; + TRACE( "himc %p\n", himc ); + + if (!imc) return NULL; + imc->dwLock++; + + imc_select_hkl( imc, GetKeyboardLayout( 0 ) ); + return &imc->IMC; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 5dde6cd1b1f..88311a16c96 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3790,7 +3790,6 @@ static void test_ImmActivateLayout(void) { .hkl = expect_ime, .himc = default_himc, .func = IME_SELECT, .select = 1, - .todo = TRUE, }, {0}, }; @@ -3808,6 +3807,15 @@ static void test_ImmActivateLayout(void) }, {0}, }; + const struct ime_call activate_with_window_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_SELECT, .select = 1, + .todo = TRUE, + }, + {0}, + }; HKL hkl, old_hkl = GetKeyboardLayout( 0 ); UINT ret; @@ -3835,7 +3843,6 @@ static void test_ImmActivateLayout(void) SET_EXPECT( ImeInquire ); ok_ret( 1, ImmActivateLayout( hkl ) ); ok_seq( activate_seq ); - todo_wine CHECK_CALLED( ImeInquire ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -3843,8 +3850,10 @@ static void test_ImmActivateLayout(void) ok_ret( 1, ImmActivateLayout( hkl ) ); ok_seq( empty_sequence ); + todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_ret( 1, ImmActivateLayout( old_hkl ) ); ok_seq( deactivate_seq ); + todo_ImeDestroy = FALSE; ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -3857,7 +3866,6 @@ static void test_ImmActivateLayout(void) SET_EXPECT( ImeDestroy ); ret = ImmFreeLayout( hkl ); ok( ret, "ImmFreeLayout returned %u\n", ret ); - todo_wine CHECK_CALLED( ImeDestroy ); ok_seq( empty_sequence ); @@ -3875,7 +3883,7 @@ static void test_ImmActivateLayout(void) ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); todo_wine CHECK_CALLED( ImeInquire ); - ok_seq( activate_seq ); + ok_seq( activate_with_window_seq ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -3911,12 +3919,12 @@ static void test_ImmCreateInputContext(void) { .hkl = expect_ime, .himc = default_himc, .func = IME_SELECT, .select = 1, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, { .hkl = expect_ime, .himc = 0/*himc[0]*/, .func = IME_SELECT, .select = 1, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, {0}, }; From 93048bc1eb2a3ef05ad6808fc53d95c0c6a7a892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 29 Mar 2023 14:18:38 +0200 Subject: [PATCH 161/758] imm32/tests: Add explicit ImmLoadIME / ImmFreeLayout calls. (cherry picked from commit 4b04d357739f261457316d55b79ecbcff9c0be84) --- dlls/imm32/tests/imm32.c | 45 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 88311a16c96..ef4b77d0573 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2973,7 +2973,7 @@ static HKL ime_install(void) return hkl; } -static void ime_cleanup( HKL hkl ) +static void ime_cleanup( HKL hkl, BOOL free ) { HMODULE module = GetModuleHandleW( L"winetest_ime.dll" ); WCHAR buffer[MAX_PATH], value[MAX_PATH]; @@ -2984,6 +2984,8 @@ static void ime_cleanup( HKL hkl ) todo_wine ok( ret, "UnloadKeyboardLayout failed, error %lu\n", GetLastError() ); + if (free) ok_ret( 1, ImmFreeLayout( hkl ) ); + swprintf( buffer, ARRAY_SIZE(buffer), L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08x", hkl ); ret = RegDeleteKeyW( HKEY_LOCAL_MACHINE, buffer ); ok( !ret, "RegDeleteKeyW returned %#lx, error %lu\n", ret, GetLastError() ); @@ -3086,7 +3088,7 @@ static void test_ImmInstallIME(void) ret = ImmFreeLayout( hkl ); ok( ret, "ImmFreeLayout returned %#x\n", ret ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); ime_info.fdwProperty = 0; @@ -3112,7 +3114,7 @@ static void test_ImmInstallIME(void) ret = ImmFreeLayout( hkl ); ok( ret, "ImmFreeLayout returned %#x\n", ret ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); @@ -3151,7 +3153,7 @@ static void test_ImmIsIME(void) todo_ImeInquire = FALSE; todo_ImeDestroy = FALSE; - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); @@ -3246,7 +3248,7 @@ static void test_ImmGetProperty(void) todo_ImeDestroy = FALSE; called_ImeDestroy = FALSE; - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( ImeInquire, FALSE ); @@ -3319,7 +3321,7 @@ static void test_ImmGetDescription(void) ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret ); ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); @@ -3398,7 +3400,7 @@ static void test_ImmGetIMEFileName(void) ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); @@ -3506,7 +3508,7 @@ static void test_ImmEscape( BOOL unicode ) winetest_pop_context(); } - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( ImeEscape, FALSE ); @@ -3574,7 +3576,7 @@ static void test_ImmEnumRegisterWord( BOOL unicode ) ok_ret( 0xdeadbeef, ImmEnumRegisterWordA( hkl, enum_register_wordA, "Reading", 0xdeadbeef, "String", NULL ) ); CHECK_CALLED( ImeEnumRegisterWord ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( ImeEnumRegisterWord, FALSE ); @@ -3636,7 +3638,7 @@ static void test_ImmRegisterWord( BOOL unicode ) ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, "String" ) ); CHECK_CALLED( ImeRegisterWord ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( ImeRegisterWord, FALSE ); @@ -3713,7 +3715,7 @@ static void test_ImmGetRegisterWordStyle( BOOL unicode ) } CHECK_CALLED( ImeGetRegisterWordStyle ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( ImeGetRegisterWordStyle, FALSE ); @@ -3775,7 +3777,7 @@ static void test_ImmUnregisterWord( BOOL unicode ) ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, "String" ) ); CHECK_CALLED( ImeUnregisterWord ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); cleanup: SET_ENABLE( ImeUnregisterWord, FALSE ); @@ -3844,6 +3846,7 @@ static void test_ImmActivateLayout(void) ok_ret( 1, ImmActivateLayout( hkl ) ); ok_seq( activate_seq ); CHECK_CALLED( ImeInquire ); + ok_ret( 1, ImmLoadIME( hkl ) ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -3857,7 +3860,7 @@ static void test_ImmActivateLayout(void) ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ime_cleanup( hkl ); + ime_cleanup( hkl, FALSE ); ok_seq( empty_sequence ); @@ -3884,6 +3887,9 @@ static void test_ImmActivateLayout(void) todo_wine CHECK_CALLED( ImeInquire ); ok_seq( activate_with_window_seq ); + todo_ImeInquire = TRUE; + ok_ret( 1, ImmLoadIME( hkl ) ); + todo_ImeInquire = FALSE; ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -3892,15 +3898,8 @@ static void test_ImmActivateLayout(void) ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ime_cleanup( hkl ); - ok_seq( empty_sequence ); - - /* ImmActivateLayout leaks the IME, we need to free it manually */ - SET_EXPECT( ImeDestroy ); - ret = ImmFreeLayout( hkl ); - ok( ret, "ImmFreeLayout returned %u\n", ret ); - todo_wine + ime_cleanup( hkl, TRUE ); CHECK_CALLED( ImeDestroy ); ok_seq( empty_sequence ); @@ -4007,6 +4006,7 @@ static void test_ImmCreateInputContext(void) if (!(hkl = ime_install())) goto cleanup; + ok_ret( 1, ImmLoadIME( hkl ) ); /* Activating the layout calls ImeSelect 1 on existing HIMC */ @@ -4055,8 +4055,7 @@ static void test_ImmCreateInputContext(void) ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ok_ret( 1, ImmFreeLayout( hkl ) ); - ime_cleanup( hkl ); + ime_cleanup( hkl, TRUE ); ok_seq( empty_sequence ); cleanup: From dd3bd4f60382065d694b65216f0eb1c5d7aad0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 10:12:04 +0200 Subject: [PATCH 162/758] imm32/tests: Ignore expected calls marked with todo. (cherry picked from commit 5a6ed25f6c29f0ee98c44185f4602836748ec4d0) --- dlls/imm32/tests/imm32.c | 42 +++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ef4b77d0573..c903b9ca115 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2519,7 +2519,7 @@ static struct ime_call ime_calls[1024]; static ULONG ime_call_count; #define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b ) -static void ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received ) +static int ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received ) { int ret; @@ -2546,13 +2546,13 @@ static void ok_call_( const char *file, int line, const struct ime_call *expecte case IME_SELECT: todo_wine_if( expected->todo ) ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select ); - return; + return ret; case IME_NOTIFY: todo_wine_if( expected->todo ) ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", received->hkl, received->himc, received->notify.action, received->notify.index, received->notify.value ); - return; + return ret; } switch (expected->func) @@ -2568,21 +2568,28 @@ static void ok_call_( const char *file, int line, const struct ime_call *expecte expected->notify.value ); break; } + + return 0; } #define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a ) static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context ) { const struct ime_call *received = ime_calls; - UINT i = 0; + UINT i = 0, ret; while (expected->func || received->func) { winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "", !received->func ? " (missing)" : "" ); - ok_call_( file, line, expected, received ); - if (expected->func) expected++; - if (received->func) received++; + ret = ok_call_( file, line, expected, received ); + if (ret && expected->todo && !strcmp( winetest_platform, "wine" )) + expected++; + else + { + if (expected->func) expected++; + if (received->func) received++; + } winetest_pop_context(); } @@ -3805,7 +3812,6 @@ static void test_ImmActivateLayout(void) { .hkl = default_hkl, .himc = default_himc, .func = IME_SELECT, .select = 0, - .todo = TRUE, }, {0}, }; @@ -3818,6 +3824,20 @@ static void test_ImmActivateLayout(void) }, {0}, }; + const struct ime_call deactivate_with_window_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, + .todo = TRUE, + }, + { + .hkl = default_hkl, .himc = default_himc, + .func = IME_SELECT, .select = 0, + .todo = TRUE, + }, + {0}, + }; HKL hkl, old_hkl = GetKeyboardLayout( 0 ); UINT ret; @@ -3894,7 +3914,7 @@ static void test_ImmActivateLayout(void) ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); - ok_seq( deactivate_seq ); + ok_seq( deactivate_with_window_seq ); ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -3959,12 +3979,12 @@ static void test_ImmCreateInputContext(void) { .hkl = default_hkl, .himc = default_himc, .func = IME_SELECT, .select = 0, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, { .hkl = default_hkl, .himc = 0/*himc[0]*/, .func = IME_SELECT, .select = 0, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, {0}, }; From 66c0bc88a515405f8e205822c89fdc7c330d5dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:55:44 +0200 Subject: [PATCH 163/758] imm32: Cleanup ImmProcessKey variables and traces. (cherry picked from commit 3a07b09079fff7986eb68108eb09a234d50e1e98) --- dlls/imm32/imm.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 2c3af063ac5..e3c7b3d579b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3025,27 +3025,24 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD * ImmProcessKey(IMM32.@) * ( Undocumented, called from user32.dll ) */ -BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD unknown) +BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM lparam, DWORD unknown ) { - struct imc *data; - HIMC imc = ImmGetContext(hwnd); + struct imc *imc; BYTE state[256]; + BOOL ret; - TRACE("%p %p %x %x %lx\n",hwnd, hKL, vKey, (UINT)lKeyData, unknown); + TRACE( "hwnd %p, hkl %p, vkey %#x, lparam %#Ix, unknown %#lx\n", hwnd, hkl, vkey, lparam, unknown ); - if (!(data = get_imc_data( imc ))) return FALSE; - imc_select_hkl( data, hKL ); - if (!data->ime) return FALSE; + if (!(imc = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; + imc_select_hkl( imc, hkl ); + if (!imc->ime) return FALSE; - GetKeyboardState(state); - if (data->ime->pImeProcessKey( imc, vKey, lKeyData, state )) - { - data->lastVK = vKey; - return TRUE; - } + GetKeyboardState( state ); - data->lastVK = VK_PROCESSKEY; - return FALSE; + ret = imc->ime->pImeProcessKey( imc->handle, vkey, lparam, state ); + imc->lastVK = ret ? vkey : VK_PROCESSKEY; + + return ret; } /*********************************************************************** From 654cb13734f431c67e21df1641656edae18d00bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:56:53 +0200 Subject: [PATCH 164/758] imm32: Ignore ImmProcessKey if hkl isn't the current layout. (cherry picked from commit bda3ee0bd5b5083c34aa5d74ccc4db6f1cc03fdc) --- dlls/imm32/imm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e3c7b3d579b..447fbded41d 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3033,6 +3033,7 @@ BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM lparam, DWORD u TRACE( "hwnd %p, hkl %p, vkey %#x, lparam %#Ix, unknown %#lx\n", hwnd, hkl, vkey, lparam, unknown ); + if (hkl != GetKeyboardLayout( 0 )) return FALSE; if (!(imc = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; imc_select_hkl( imc, hkl ); if (!imc->ime) return FALSE; From 11364bad88a4fb1b12569852fa5411814e9236dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 10:26:14 +0200 Subject: [PATCH 165/758] imm32/tests: Test ImmProcessKey with the installed IME. (cherry picked from commit 9117ce4185146a5edd47467d370b3e4dfa588191) --- dlls/imm32/tests/imm32.c | 84 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index c903b9ca115..58bdeda848e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2491,6 +2491,7 @@ enum ime_function { IME_SELECT = 1, IME_NOTIFY, + IME_PROCESS_KEY, }; struct ime_call @@ -2508,6 +2509,11 @@ struct ime_call int index; int value; } notify; + struct + { + WORD vkey; + LPARAM key_data; + } process_key; }; BOOL todo; @@ -2538,6 +2544,10 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected if ((ret = expected->notify.index - received->notify.index)) goto done; if ((ret = expected->notify.value - received->notify.value)) goto done; break; + case IME_PROCESS_KEY: + if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done; + if ((ret = expected->process_key.key_data - received->process_key.key_data)) goto done; + break; } done: @@ -2553,6 +2563,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected received->hkl, received->himc, received->notify.action, received->notify.index, received->notify.value ); return ret; + case IME_PROCESS_KEY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", + received->hkl, received->himc, received->process_key.vkey, received->process_key.key_data ); + return ret; } switch (expected->func) @@ -2567,6 +2582,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected expected->hkl, expected->himc, expected->notify.action, expected->notify.index, expected->notify.value ); break; + case IME_PROCESS_KEY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", + expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.key_data ); + break; } return 0; @@ -2764,11 +2784,15 @@ static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) { + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = himc, + .func = IME_PROCESS_KEY, .process_key = {.vkey = vkey, .key_data = key_data} + }; ime_trace( "himc %p, vkey %u, key_data %#Ix, key_state %p\n", himc, vkey, key_data, key_state ); - ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ok( 0, "unexpected call\n" ); - return FALSE; + ime_calls[ime_call_count++] = call; + return TRUE; } static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) @@ -3792,6 +3816,59 @@ static void test_ImmUnregisterWord( BOOL unicode ) winetest_pop_context(); } +static void test_ImmProcessKey(void) +{ + const struct ime_call process_key_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .key_data = 0}, + }, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + UINT_PTR ret; + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) ); + ok_seq( empty_sequence ); + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 0, ImmProcessKey( 0, hkl, 'A', 0, 0 ) ); + ok_seq( empty_sequence ); + + ret = ImmProcessKey( hwnd, hkl, 'A', 0, 0 ); + todo_wine + ok_ret( 2, ret ); + ok_seq( process_key_seq ); + + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) ); + todo_wine + ok_seq( empty_sequence ); + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + +cleanup: + ok_ret( 1, DestroyWindow( hwnd ) ); +} + static void test_ImmActivateLayout(void) { const struct ime_call activate_seq[] = @@ -4118,6 +4195,7 @@ START_TEST(imm32) test_ImmActivateLayout(); test_ImmCreateInputContext(); + test_ImmProcessKey(); if (init()) { From 9be67ede1b59e754e7f56bfa3b9684c5275e4e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 29 Mar 2023 15:06:51 +0200 Subject: [PATCH 166/758] imm32/tests: Test IME UI creation with the installed IME. (cherry picked from commit 42f3cf1bf3a48ce62b8f4241348f3139fbeeff76) --- dlls/imm32/tests/imm32.c | 176 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 58bdeda848e..079338a044f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -118,6 +118,17 @@ extern BOOL WINAPI ImmActivateLayout(HKL); DEFINE_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE); DEFINE_EXPECT(WM_IME_SETCONTEXT_ACTIVATE); +static void process_messages(void) +{ + MSG msg; + + while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + } +} + /* * msgspy - record and analyse message traces sent to a certain window */ @@ -2492,6 +2503,7 @@ enum ime_function IME_SELECT = 1, IME_NOTIFY, IME_PROCESS_KEY, + MSG_IME_UI, }; struct ime_call @@ -2514,9 +2526,16 @@ struct ime_call WORD vkey; LPARAM key_data; } process_key; + struct + { + UINT msg; + WPARAM wparam; + LPARAM lparam; + } message; }; BOOL todo; + BOOL broken; BOOL flaky_himc; }; @@ -2548,9 +2567,16 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done; if ((ret = expected->process_key.key_data - received->process_key.key_data)) goto done; break; + case MSG_IME_UI: + if ((ret = expected->message.msg - received->message.msg)) goto done; + if ((ret = (expected->message.wparam - received->message.wparam))) goto done; + if ((ret = (expected->message.lparam - received->message.lparam))) goto done; + break; } done: + if (ret && broken( expected->broken )) return ret; + switch (received->func) { case IME_SELECT: @@ -2568,6 +2594,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", received->hkl, received->himc, received->process_key.vkey, received->process_key.key_data ); return ret; + case MSG_IME_UI: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, + received->himc, received->message.msg, received->message.wparam, received->message.lparam ); + return ret; } switch (expected->func) @@ -2587,6 +2618,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.key_data ); break; + case MSG_IME_UI: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, + expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); + break; } return 0; @@ -2605,6 +2641,8 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected ret = ok_call_( file, line, expected, received ); if (ret && expected->todo && !strcmp( winetest_platform, "wine" )) expected++; + else if (ret && broken(expected->broken)) + expected++; else { if (expected->func) expected++; @@ -2617,9 +2655,46 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected ime_call_count = 0; } +static BOOL ignore_message( UINT msg ) +{ + switch (msg) + { + case WM_IME_STARTCOMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_COMPOSITION: + case WM_IME_SETCONTEXT: + case WM_IME_NOTIFY: + case WM_IME_CONTROL: + case WM_IME_COMPOSITIONFULL: + case WM_IME_SELECT: + case WM_IME_CHAR: + case 0x287: + case WM_IME_REQUEST: + case WM_IME_KEYDOWN: + case WM_IME_KEYUP: + return FALSE; + default: + return TRUE; + } +} + static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ), + .func = MSG_IME_UI, .message = {.msg = msg, .wparam = wparam, .lparam = lparam} + }; + LONG_PTR ptr; + ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + + ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE ); + ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr ); + + ime_calls[ime_call_count++] = call; return DefWindowProcW( hwnd, msg, wparam, lparam ); } @@ -2849,7 +2924,6 @@ static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, { ime_trace( "vkey %u, scan_code %u, key_state %p, msgs %p, state %u, himc %p\n", vkey, scan_code, key_state, msgs, state, himc ); - ok( 0, "unexpected call\n" ); return 0; } @@ -3832,6 +3906,7 @@ static void test_ImmProcessKey(void) hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + process_messages(); ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) ); ok_seq( empty_sequence ); @@ -3842,6 +3917,7 @@ static void test_ImmProcessKey(void) ok_ret( 1, ImmActivateLayout( hkl ) ); ok_ret( 1, ImmLoadIME( hkl ) ); + process_messages(); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -3862,6 +3938,7 @@ static void test_ImmProcessKey(void) ok_ret( 1, ImmActivateLayout( old_hkl ) ); ime_cleanup( hkl, TRUE ); + process_messages(); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -3892,30 +3969,76 @@ static void test_ImmActivateLayout(void) }, {0}, }; - const struct ime_call activate_with_window_seq[] = + struct ime_call activate_with_window_seq[] = { { .hkl = expect_ime, .himc = default_himc, .func = IME_SELECT, .select = 1, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_SELECT, .select = 1, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + .todo = TRUE, .broken = (default_hkl == (HKL)0x04120412), + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, .todo = TRUE, }, {0}, }; - const struct ime_call deactivate_with_window_seq[] = + struct ime_call deactivate_with_window_seq[] = { { .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime}, .todo = TRUE, }, { .hkl = default_hkl, .himc = default_himc, .func = IME_SELECT, .select = 0, - .todo = TRUE, + .todo = TRUE, .flaky_himc = TRUE, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, + .func = IME_SELECT, .select = 0, + .todo = TRUE, .flaky_himc = TRUE, }, {0}, }; HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HIMC himc; UINT ret; SET_ENABLE( ImeInquire, TRUE ); @@ -3977,12 +4100,18 @@ static void test_ImmActivateLayout(void) hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + process_messages(); + ok_seq( empty_sequence ); + + himc = ImmCreateContext(); + ok( !!himc, "got himc %p\n", himc ); ok_seq( empty_sequence ); SET_EXPECT( ImeInquire ); ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); todo_wine CHECK_CALLED( ImeInquire ); + activate_with_window_seq[1].himc = himc; ok_seq( activate_with_window_seq ); todo_ImeInquire = TRUE; ok_ret( 1, ImmLoadIME( hkl ) ); @@ -3990,17 +4119,30 @@ static void test_ImmActivateLayout(void) ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + /* FIXME: ignore spurious VK_CONTROL ImeProcessKey / ImeToAsciiEx calls */ + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + deactivate_with_window_seq[1].himc = himc; + deactivate_with_window_seq[5].himc = himc; ok_seq( deactivate_with_window_seq ); ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_ret( 1, ImmDestroyContext( himc ) ); + ok_seq( empty_sequence ); + SET_EXPECT( ImeDestroy ); ime_cleanup( hkl, TRUE ); CHECK_CALLED( ImeDestroy ); ok_seq( empty_sequence ); ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; cleanup: @@ -4022,6 +4164,16 @@ static void test_ImmCreateInputContext(void) .func = IME_SELECT, .select = 1, .flaky_himc = TRUE, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW}, + .todo = TRUE, + }, {0}, }; struct ime_call select1_seq[] = @@ -4053,6 +4205,16 @@ static void test_ImmCreateInputContext(void) .func = IME_NOTIFY, .notify = {.action = NI_COMPOSITIONSTR, .index = CPS_CANCEL, .value = 0}, .todo = TRUE, .flaky_himc = TRUE, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime}, + .todo = TRUE, + }, { .hkl = default_hkl, .himc = default_himc, .func = IME_SELECT, .select = 0, @@ -4088,6 +4250,7 @@ static void test_ImmCreateInputContext(void) hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + process_messages(); ctx = ImmLockIMC( default_himc ); ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); @@ -4147,7 +4310,7 @@ static void test_ImmCreateInputContext(void) ok_ret( 1, ImmActivateLayout( old_hkl ) ); deactivate_seq[1].himc = himc[0]; - deactivate_seq[3].himc = himc[0]; + deactivate_seq[5].himc = himc[0]; ok_seq( deactivate_seq ); ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -4158,6 +4321,9 @@ static void test_ImmCreateInputContext(void) cleanup: ok_ret( 1, ImmDestroyContext( himc[0] ) ); ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; ime_info.dwPrivateDataSize = 0; } From b5bc992a1977ef9808881f0bb77eae34c92a120e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 29 Mar 2023 11:24:14 +0200 Subject: [PATCH 167/758] imm32/tests: Test IME UI window and IME window presence. (cherry picked from commit 43e22eaa76a443e3053868df1bd8c5d4130a9fde) --- dlls/imm32/tests/imm32.c | 46 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 079338a044f..305e152886d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3946,6 +3946,37 @@ static void test_ImmProcessKey(void) ok_ret( 1, DestroyWindow( hwnd ) ); } +struct ime_windows +{ + HWND ime_hwnd; + HWND ime_ui_hwnd; +}; + +static BOOL CALLBACK enum_thread_ime_windows( HWND hwnd, LPARAM lparam ) +{ + struct ime_windows *params = (void *)lparam; + WCHAR buffer[256]; + UINT ret; + + ime_trace( "hwnd %p, lparam %#Ix\n", hwnd, lparam ); + + ret = RealGetWindowClassW( hwnd, buffer, ARRAY_SIZE(buffer) ); + ok( ret, "RealGetWindowClassW returned %#x\n", ret ); + + if (!wcscmp( buffer, L"IME" )) + { + ok( !params->ime_hwnd, "Found extra IME window %p\n", hwnd ); + params->ime_hwnd = hwnd; + } + if (!wcscmp( buffer, ime_ui_class.lpszClassName )) + { + ok( !params->ime_ui_hwnd, "Found extra IME UI window %p\n", hwnd ); + params->ime_ui_hwnd = hwnd; + } + + return TRUE; +} + static void test_ImmActivateLayout(void) { const struct ime_call activate_seq[] = @@ -4038,6 +4069,7 @@ static void test_ImmActivateLayout(void) {0}, }; HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + struct ime_windows ime_windows = {0}; HIMC himc; UINT ret; @@ -4134,10 +4166,22 @@ static void test_ImmActivateLayout(void) ok_ret( 1, ImmDestroyContext( himc ) ); ok_seq( empty_sequence ); + + ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); + ok( !!ime_windows.ime_hwnd, "missing IME window\n" ); + todo_wine ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" ); + todo_wine ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); + + ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + + SET_EXPECT( ImeDestroy ); ime_cleanup( hkl, TRUE ); CHECK_CALLED( ImeDestroy ); - ok_seq( empty_sequence ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); From 652368f77f1480e58805c2a6fc19097d932509d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 11:24:21 +0200 Subject: [PATCH 168/758] imm32: Update existing input contexts on layout change. (cherry picked from commit 0ddad3564f3260f53e5a50b615f6aa6ba27e032b) --- dlls/imm32/imm.c | 5 +++++ dlls/imm32/tests/imm32.c | 15 ++++++++++----- dlls/win32u/input.c | 8 +++++++- include/ntuser.h | 2 ++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 447fbded41d..62fc855826b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3152,6 +3152,11 @@ static LRESULT ime_internal_msg( WPARAM wparam, LPARAM lparam) ImmSetActiveContext(hwnd, himc, wparam == IME_INTERNAL_ACTIVATE); ImmReleaseContext(hwnd, himc); break; + case IME_INTERNAL_HKL_ACTIVATE: + ImmEnumInputContext( 0, enum_activate_layout, 0 ); + break; + case IME_INTERNAL_HKL_DEACTIVATE: + break; default: FIXME("wparam = %Ix\n", wparam); break; diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 305e152886d..8d3a4ce1529 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4005,12 +4005,12 @@ static void test_ImmActivateLayout(void) { .hkl = expect_ime, .himc = default_himc, .func = IME_SELECT, .select = 1, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_SELECT, .select = 1, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, { .hkl = expect_ime, .himc = default_himc, @@ -4059,12 +4059,12 @@ static void test_ImmActivateLayout(void) { .hkl = default_hkl, .himc = default_himc, .func = IME_SELECT, .select = 0, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, { .hkl = default_hkl, .himc = 0/*himc*/, .func = IME_SELECT, .select = 0, - .todo = TRUE, .flaky_himc = TRUE, + .flaky_himc = TRUE, }, {0}, }; @@ -4141,7 +4141,6 @@ static void test_ImmActivateLayout(void) SET_EXPECT( ImeInquire ); ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); - todo_wine CHECK_CALLED( ImeInquire ); activate_with_window_seq[1].himc = himc; ok_seq( activate_with_window_seq ); @@ -4156,7 +4155,9 @@ static void test_ImmActivateLayout(void) memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; + todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + todo_ImeDestroy = FALSE; deactivate_with_window_seq[1].himc = himc; deactivate_with_window_seq[5].himc = himc; ok_seq( deactivate_with_window_seq ); @@ -4167,7 +4168,9 @@ static void test_ImmActivateLayout(void) ok_seq( empty_sequence ); + todo_ImeInquire = TRUE; ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + todo_ImeInquire = FALSE; ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); @@ -4175,7 +4178,9 @@ static void test_ImmActivateLayout(void) todo_wine ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" ); todo_wine ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); + todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + todo_ImeDestroy = FALSE; ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index a983ac0b706..289c8c2fde4 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1221,12 +1221,14 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) return 0; old_layout = info->kbd_layout; - info->kbd_layout = layout; if (old_layout != layout) { + HWND ime_hwnd = get_default_ime_window( 0 ); const NLS_LOCALE_DATA *data; CHARSETINFO cs = {0}; + if (ime_hwnd) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_HKL_DEACTIVATE, HandleToUlong(old_layout) ); + if (HIWORD(layout) & 0x8000) FIXME( "Aliased keyboard layout not yet implemented\n" ); else if (!(data = get_locale_data( HIWORD(layout) ))) @@ -1234,7 +1236,11 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) else translate_charset_info( ULongToPtr(data->idefaultansicodepage), &cs, TCI_SRCCODEPAGE ); + info->kbd_layout = layout; info->kbd_layout_id = 0; + + if (ime_hwnd) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_HKL_ACTIVATE, HandleToUlong(layout) ); + if ((focus = get_focus()) && get_window_thread( focus, NULL ) == GetCurrentThreadId()) send_message( focus, WM_INPUTLANGCHANGE, cs.ciCharset, (LPARAM)layout ); } diff --git a/include/ntuser.h b/include/ntuser.h index 0771814c999..86267d3e244 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -487,6 +487,8 @@ enum wine_internal_message #define WM_IME_INTERNAL 0x287 #define IME_INTERNAL_ACTIVATE 0x17 #define IME_INTERNAL_DEACTIVATE 0x18 +#define IME_INTERNAL_HKL_ACTIVATE 0x19 +#define IME_INTERNAL_HKL_DEACTIVATE 0x20 #define WM_SYSTIMER 0x0118 From 22d57272d1fe636646861ee3b576ad0cf68c1c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 27 Mar 2023 11:58:50 +0200 Subject: [PATCH 169/758] imm32: Keep the IME UI window on the default input context. (cherry picked from commit 2504a2d7bca47fc77bb523114d7b0b6e60213116) --- dlls/imm32/imm.c | 61 +++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 62fc855826b..d68930bade3 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -62,7 +62,6 @@ struct ime IMEINFO info; WCHAR ui_class[17]; - HWND ui_hwnd; BOOL (WINAPI *pImeInquire)(IMEINFO *, void *, DWORD); BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *); @@ -93,6 +92,8 @@ struct imc struct ime *ime; UINT lastVK; + + HWND ui_hwnd; /* IME UI window, on the default input context */ }; #define WINE_IMC_VALID_MAGIC 0x56434D49 @@ -569,6 +570,8 @@ static void imc_select_hkl( struct imc *imc, HKL hkl ) if (imc->ime) { if (imc->ime->hkl == hkl) return; + if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd ); + imc->ui_hwnd = NULL; imc->ime->pImeSelect( imc->handle, FALSE ); ime_release( imc->ime ); ImmDestroyIMCC( imc->IMC.hPrivate ); @@ -614,6 +617,7 @@ static BOOL free_input_context_data( HIMC hIMC ) TRACE( "Destroying %p\n", hIMC ); + if (data->ui_hwnd) DestroyWindow( data->ui_hwnd ); data->ime->pImeSelect( hIMC, FALSE ); SendMessageW( data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->ime ); @@ -648,7 +652,6 @@ static void IMM_FreeAllImmHkl(void) ime->pImeDestroy( 1 ); FreeLibrary( ime->module ); - if (ime->ui_hwnd) DestroyWindow( ime->ui_hwnd ); free( ime ); } } @@ -923,6 +926,29 @@ static struct imc *get_imc_data( HIMC handle ) return create_input_context(handle); } +static struct imc *default_input_context(void) +{ + UINT *himc = &NtUserGetThreadInfo()->default_imc; + if (!*himc) *himc = (UINT_PTR)NtUserCreateInputContext( 0 ); + return get_imc_data( (HIMC)(UINT_PTR)*himc ); +} + +static HWND get_ime_ui_window(void) +{ + struct imc *imc = default_input_context(); + + imc_select_hkl( imc, GetKeyboardLayout( 0 ) ); + if (!imc->ime) return 0; + + if (!imc->ui_hwnd) + { + imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, imc->ime->ui_class, NULL, + WS_POPUP, 0, 0, 1, 1, 0, 0, imc->ime->module, 0 ); + SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)imc->handle ); + } + return imc->ui_hwnd; +} + /*********************************************************************** * ImmCreateContext (IMM32.@) */ @@ -2490,6 +2516,7 @@ BOOL WINAPI ImmSetCompositionWindow( { BOOL reshow = FALSE; struct imc *data = get_imc_data( hIMC ); + HWND ui_hwnd; TRACE("(%p, %p)\n", hIMC, lpCompForm); if (lpCompForm) @@ -2507,15 +2534,15 @@ BOOL WINAPI ImmSetCompositionWindow( data->IMC.cfCompForm = *lpCompForm; - if (IsWindowVisible( data->ime->ui_hwnd )) + if ((ui_hwnd = get_ime_ui_window()) && IsWindowVisible( ui_hwnd )) { reshow = TRUE; - ShowWindow( data->ime->ui_hwnd, SW_HIDE ); + ShowWindow( ui_hwnd, SW_HIDE ); } /* FIXME: this is a partial stub */ - if (reshow) ShowWindow( data->ime->ui_hwnd, SW_SHOWNOACTIVATE ); + if (ui_hwnd && reshow) ShowWindow( ui_hwnd, SW_SHOWNOACTIVATE ); ImmInternalSendIMENotify(data, IMN_SETCOMPOSITIONWINDOW, 0); return TRUE; @@ -2564,6 +2591,7 @@ BOOL WINAPI ImmSetConversionStatus( BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) { struct imc *data = get_imc_data( hIMC ); + HWND ui_hwnd; TRACE("%p %d\n", hIMC, fOpen); @@ -2575,14 +2603,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; - if (data->ime->ui_hwnd == NULL) - { - /* create the ime window */ - data->ime->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, data->ime->ui_class, NULL, - WS_POPUP, 0, 0, 1, 1, 0, 0, data->ime->module, 0 ); - SetWindowLongPtrW( data->ime->ui_hwnd, IMMGWL_IMC, (LONG_PTR)data ); - } - else if (fOpen) SetWindowLongPtrW( data->ime->ui_hwnd, IMMGWL_IMC, (LONG_PTR)data ); + if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)data ); if (!fOpen != !data->IMC.fOpen) { @@ -3099,18 +3120,6 @@ BOOL WINAPI ImmDisableLegacyIME(void) return TRUE; } -static HWND get_ui_window(HKL hkl) -{ - struct ime *ime; - HWND hwnd; - - if (!(ime = ime_acquire( hkl ))) return 0; - hwnd = ime->ui_hwnd; - ime_release( ime ); - - return hwnd; -} - static BOOL is_ime_ui_msg(UINT msg) { switch (msg) @@ -3205,7 +3214,7 @@ LRESULT WINAPI __wine_ime_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp if (is_ime_ui_msg(msg)) { - if ((ui_hwnd = get_ui_window(NtUserGetKeyboardLayout(0)))) + if ((ui_hwnd = get_ime_ui_window())) { if (ansi) return SendMessageA(ui_hwnd, msg, wparam, lparam); From f7e10807972b283098663ccb4a96e83c48098bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:38:14 +0200 Subject: [PATCH 170/758] imm32: Re-create the IME UI window when IME changes. (cherry picked from commit bb2414fdb15a074786d463376d3bf5f17f73c459) --- dlls/imm32/imm.c | 5 ++++- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index d68930bade3..ed903cb8d00 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3150,21 +3150,24 @@ static BOOL is_ime_ui_msg(UINT msg) static LRESULT ime_internal_msg( WPARAM wparam, LPARAM lparam) { - HWND hwnd = (HWND)lparam; + HWND hwnd; HIMC himc; switch (wparam) { case IME_INTERNAL_ACTIVATE: case IME_INTERNAL_DEACTIVATE: + hwnd = (HWND)lparam; himc = ImmGetContext(hwnd); ImmSetActiveContext(hwnd, himc, wparam == IME_INTERNAL_ACTIVATE); ImmReleaseContext(hwnd, himc); break; case IME_INTERNAL_HKL_ACTIVATE: ImmEnumInputContext( 0, enum_activate_layout, 0 ); + hwnd = get_ime_ui_window(); break; case IME_INTERNAL_HKL_DEACTIVATE: + hwnd = get_ime_ui_window(); break; default: FIXME("wparam = %Ix\n", wparam); diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 8d3a4ce1529..23cc9f4b906 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4175,7 +4175,7 @@ static void test_ImmActivateLayout(void) ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); ok( !!ime_windows.ime_hwnd, "missing IME window\n" ); - todo_wine ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" ); + ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" ); todo_wine ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ From 39d607098d0a90120ec24ab287272ffc38f7f338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 29 Mar 2023 11:39:41 +0200 Subject: [PATCH 171/758] imm32: Create the IME UI as child of the IME default window. (cherry picked from commit 585ac559b78da2a6d08b830b2f3e2310f50dc3e7) --- dlls/imm32/imm.c | 4 ++-- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index ed903cb8d00..4cbdceeb9d9 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -942,8 +942,8 @@ static HWND get_ime_ui_window(void) if (!imc->ui_hwnd) { - imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, imc->ime->ui_class, NULL, - WS_POPUP, 0, 0, 1, 1, 0, 0, imc->ime->module, 0 ); + imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, imc->ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, + ImmGetDefaultIMEWnd( 0 ), 0, imc->ime->module, 0 ); SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)imc->handle ); } return imc->ui_hwnd; diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 23cc9f4b906..dfc65cbec24 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4176,7 +4176,7 @@ static void test_ImmActivateLayout(void) ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); ok( !!ime_windows.ime_hwnd, "missing IME window\n" ); ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" ); - todo_wine ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); + ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); From b86e5e17a23437f11bf353d6085326381843ad66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:01:54 +0200 Subject: [PATCH 172/758] imm32/tests: Test DefWindowProc with IME UI messages. (cherry picked from commit 5c98617e1b0c8d676c3149ac2b389c71bec934ce) --- dlls/imm32/tests/imm32.c | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index dfc65cbec24..27e830c44ec 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2497,6 +2497,7 @@ static WCHAR ime_path[MAX_PATH]; static HIMC default_himc; static HKL default_hkl; static HKL expect_ime = (HKL)(int)0xe020047f; +static BOOL skip_DefWindowProc; enum ime_function { @@ -2695,6 +2696,7 @@ static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr ); ime_calls[ime_call_count++] = call; + if (skip_DefWindowProc) return 0; return DefWindowProcW( hwnd, msg, wparam, lparam ); } @@ -4377,6 +4379,92 @@ static void test_ImmCreateInputContext(void) ime_info.dwPrivateDataSize = 0; } +static void test_DefWindowProc(void) +{ + const struct ime_call start_composition_seq[] = + { + {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION}}, + {0}, + }; + const struct ime_call end_composition_seq[] = + { + {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION}}, + {0}, + }; + const struct ime_call composition_seq[] = + { + {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION}}, + {0}, + }; + const struct ime_call set_context_seq[] = + { + {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT}}, + {0}, + }; + const struct ime_call notify_seq[] = + { + {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY}}, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + UINT_PTR ret; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) return; + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + skip_DefWindowProc = TRUE; + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_STARTCOMPOSITION, 0, 0 ) ); + ok_seq( start_composition_seq ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_ENDCOMPOSITION, 0, 0 ) ); + ok_seq( end_composition_seq ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITION, 0, 0 ) ); + ok_seq( composition_seq ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SETCONTEXT, 0, 0 ) ); + ok_seq( set_context_seq ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_NOTIFY, 0, 0 ) ); + ok_seq( notify_seq ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CONTROL, 0, 0 ) ); + todo_wine ok_seq( empty_sequence ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITIONFULL, 0, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SELECT, 0, 0 ) ); + todo_wine ok_seq( empty_sequence ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CHAR, 0, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, DefWindowProcW( hwnd, 0x287, 0, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, DefWindowProcW( hwnd, WM_IME_REQUEST, 0, 0 ) ); + ok_seq( empty_sequence ); + ret = DefWindowProcW( hwnd, WM_IME_KEYDOWN, 0, 0 ); + todo_wine + ok_ret( 0, ret ); + ok_seq( empty_sequence ); + ret = DefWindowProcW( hwnd, WM_IME_KEYUP, 0, 0 ); + todo_wine + ok_ret( 0, ret ); + ok_seq( empty_sequence ); + skip_DefWindowProc = FALSE; + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -4411,6 +4499,7 @@ START_TEST(imm32) test_ImmActivateLayout(); test_ImmCreateInputContext(); test_ImmProcessKey(); + test_DefWindowProc(); if (init()) { From b29e1ac4126dcc34c859547ed79622d6344fc648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:34:27 +0200 Subject: [PATCH 173/758] win32u: Ignore IME messages from IME UI windows in DefWindowProc. (cherry picked from commit 6fd3bd9b62f405a54db29dc5a72805063a6099ca) --- dlls/imm32/tests/imm32.c | 4 ---- dlls/win32u/defwnd.c | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 27e830c44ec..2b03b182328 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2497,7 +2497,6 @@ static WCHAR ime_path[MAX_PATH]; static HIMC default_himc; static HKL default_hkl; static HKL expect_ime = (HKL)(int)0xe020047f; -static BOOL skip_DefWindowProc; enum ime_function { @@ -2696,7 +2695,6 @@ static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr ); ime_calls[ime_call_count++] = call; - if (skip_DefWindowProc) return 0; return DefWindowProcW( hwnd, msg, wparam, lparam ); } @@ -4423,7 +4421,6 @@ static void test_DefWindowProc(void) memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; - skip_DefWindowProc = TRUE; ok_ret( 0, DefWindowProcW( hwnd, WM_IME_STARTCOMPOSITION, 0, 0 ) ); ok_seq( start_composition_seq ); ok_ret( 0, DefWindowProcW( hwnd, WM_IME_ENDCOMPOSITION, 0, 0 ) ); @@ -4454,7 +4451,6 @@ static void test_DefWindowProc(void) todo_wine ok_ret( 0, ret ); ok_seq( empty_sequence ); - skip_DefWindowProc = FALSE; ok_ret( 1, ImmActivateLayout( old_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index a7f1a11897a..51f92c38a85 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2948,7 +2948,7 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, case WM_IME_CONTROL: { HWND ime_hwnd = get_default_ime_window( hwnd ); - if (ime_hwnd) + if (ime_hwnd && ime_hwnd != NtUserGetParent( hwnd )) result = NtUserMessageCall( ime_hwnd, msg, wparam, lparam, 0, NtUserSendMessage, ansi ); } From 2cc2b63437f4fd13178f7b465992bddad9f04ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:34:27 +0200 Subject: [PATCH 174/758] win32u: Ignore some IME messages in default_window_proc. (cherry picked from commit 3e2edac438205638f84171f5dd1bf77d9abaf26c) --- dlls/imm32/tests/imm32.c | 4 ++-- dlls/win32u/defwnd.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 2b03b182328..f1645983c77 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4432,11 +4432,11 @@ static void test_DefWindowProc(void) ok_ret( 0, DefWindowProcW( hwnd, WM_IME_NOTIFY, 0, 0 ) ); ok_seq( notify_seq ); ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CONTROL, 0, 0 ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); ok_ret( 0, DefWindowProcW( hwnd, WM_IME_COMPOSITIONFULL, 0, 0 ) ); ok_seq( empty_sequence ); ok_ret( 0, DefWindowProcW( hwnd, WM_IME_SELECT, 0, 0 ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); ok_ret( 0, DefWindowProcW( hwnd, WM_IME_CHAR, 0, 0 ) ); ok_seq( empty_sequence ); ok_ret( 0, DefWindowProcW( hwnd, 0x287, 0, 0 ) ); diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 51f92c38a85..eb1c082ff20 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2943,9 +2943,7 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, case WM_IME_COMPOSITION: case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION: - case WM_IME_SELECT: case WM_IME_NOTIFY: - case WM_IME_CONTROL: { HWND ime_hwnd = get_default_ime_window( hwnd ); if (ime_hwnd && ime_hwnd != NtUserGetParent( hwnd )) From b29d551b6c8f5748b862252a44dc296d5c108d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 09:50:36 +0200 Subject: [PATCH 175/758] imm32: Pass the HIMC to the IME UI window IMMGWL_IMC. Instead of the imc pointer. (cherry picked from commit e64e4e7461c91039cb019a12880c0d6b31734772) --- dlls/imm32/imm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 4cbdceeb9d9..9b3dfc00eae 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2603,7 +2603,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; - if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)data ); + if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)hIMC ); if (!fOpen != !data->IMC.fOpen) { From 17aa1b2177f38f3154330ab88b8f663590218743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 12:43:59 +0200 Subject: [PATCH 176/758] imm32: Send WM_IME_SELECT messages when IME is activated. (cherry picked from commit 1b778dbea7350c6c2bf82850d54c7dae6839b214) --- dlls/imm32/imm.c | 8 ++++---- dlls/imm32/tests/imm32.c | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 9b3dfc00eae..d73be37736a 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -619,7 +619,6 @@ static BOOL free_input_context_data( HIMC hIMC ) if (data->ui_hwnd) DestroyWindow( data->ui_hwnd ); data->ime->pImeSelect( hIMC, FALSE ); - SendMessageW( data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->ime ); ImmDestroyIMCC( data->IMC.hCompStr ); ImmDestroyIMCC( data->IMC.hCandInfo ); @@ -912,7 +911,6 @@ static struct imc *create_input_context( HIMC default_imc ) } imc_select_hkl( new_context, GetKeyboardLayout( 0 ) ); - SendMessageW( GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->ime ); TRACE("Created context %p\n", new_context); return new_context; @@ -3164,10 +3162,12 @@ static LRESULT ime_internal_msg( WPARAM wparam, LPARAM lparam) break; case IME_INTERNAL_HKL_ACTIVATE: ImmEnumInputContext( 0, enum_activate_layout, 0 ); - hwnd = get_ime_ui_window(); + if (!(hwnd = get_ime_ui_window())) break; + SendMessageW( hwnd, WM_IME_SELECT, TRUE, lparam ); break; case IME_INTERNAL_HKL_DEACTIVATE: - hwnd = get_ime_ui_window(); + if (!(hwnd = get_ime_ui_window())) break; + SendMessageW( hwnd, WM_IME_SELECT, FALSE, lparam ); break; default: FIXME("wparam = %Ix\n", wparam); diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index f1645983c77..4d9d213cb90 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4015,7 +4015,6 @@ static void test_ImmActivateLayout(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime}, - .todo = TRUE, }, { .hkl = expect_ime, .himc = default_himc, @@ -4054,7 +4053,6 @@ static void test_ImmActivateLayout(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime}, - .todo = TRUE, }, { .hkl = default_hkl, .himc = default_himc, @@ -4216,7 +4214,6 @@ static void test_ImmCreateInputContext(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 1, .lparam = (LPARAM)expect_ime}, - .todo = TRUE, }, { .hkl = expect_ime, .himc = default_himc, @@ -4262,7 +4259,6 @@ static void test_ImmCreateInputContext(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_SELECT, .wparam = 0, .lparam = (LPARAM)expect_ime}, - .todo = TRUE, }, { .hkl = default_hkl, .himc = default_himc, From 23ee351e40b2a977b4b448bd3e3ebf8801c39876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 28 Mar 2023 13:48:55 +0200 Subject: [PATCH 177/758] imm32: Select current IME on input contexts when needed. (cherry picked from commit 538d48e3f995b8840b50cc5999c726efdf69b512) --- dlls/imm32/imm.c | 172 +++++++++++++++++++++++---------------- dlls/imm32/tests/imm32.c | 3 - 2 files changed, 102 insertions(+), 73 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index d73be37736a..b2650fcfeb9 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -565,21 +565,27 @@ static void ime_release( struct ime *ime ) LeaveCriticalSection( &ime_cs ); } -static void imc_select_hkl( struct imc *imc, HKL hkl ) +static void imc_release_ime( struct imc *imc ) { + if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd ); + imc->ui_hwnd = NULL; + imc->ime->pImeSelect( imc->handle, FALSE ); + ime_release( imc->ime ); + imc->ime = NULL; + ImmDestroyIMCC( imc->IMC.hPrivate ); +} + +static struct ime *imc_select_ime( struct imc *imc ) +{ + HKL hkl = GetKeyboardLayout( 0 ); + if (imc->ime) { - if (imc->ime->hkl == hkl) return; - if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd ); - imc->ui_hwnd = NULL; - imc->ime->pImeSelect( imc->handle, FALSE ); - ime_release( imc->ime ); - ImmDestroyIMCC( imc->IMC.hPrivate ); + if (imc->ime->hkl == hkl) return imc->ime; + imc_release_ime( imc ); } - if (!hkl) - imc->ime = NULL; - else if (!(imc->ime = ime_acquire( hkl ))) + if (!(imc->ime = ime_acquire( hkl ))) WARN( "Failed to acquire IME for HKL %p\n", hkl ); else { @@ -589,6 +595,8 @@ static void imc_select_hkl( struct imc *imc, HKL hkl ) imc->IMC.fdwSentence = imc->ime->info.fdwSentenceCaps; imc->ime->pImeSelect( imc->handle, TRUE ); } + + return imc->ime; } static BOOL CALLBACK enum_activate_layout( HIMC himc, LPARAM lparam ) @@ -617,16 +625,13 @@ static BOOL free_input_context_data( HIMC hIMC ) TRACE( "Destroying %p\n", hIMC ); - if (data->ui_hwnd) DestroyWindow( data->ui_hwnd ); - data->ime->pImeSelect( hIMC, FALSE ); + if (data->ime) imc_release_ime( data ); ImmDestroyIMCC( data->IMC.hCompStr ); ImmDestroyIMCC( data->IMC.hCandInfo ); ImmDestroyIMCC( data->IMC.hGuideLine ); - ImmDestroyIMCC( data->IMC.hPrivate ); ImmDestroyIMCC( data->IMC.hMsgBuf ); - ime_release( data->ime ); free( data ); return TRUE; @@ -731,6 +736,7 @@ static HIMCC ImmCreateBlankCompStr(void) BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) { struct imc *data = get_imc_data( himc ); + struct ime *ime; TRACE("(%p, %p, %x)\n", hwnd, himc, activate); @@ -742,7 +748,7 @@ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) if (data) { data->IMC.hWnd = activate ? hwnd : NULL; - data->ime->pImeSetActiveContext( himc, activate ); + if ((ime = imc_select_ime( data ))) ime->pImeSetActiveContext( himc, activate ); } if (IsWindow(hwnd)) @@ -910,8 +916,6 @@ static struct imc *create_input_context( HIMC default_imc ) return 0; } - imc_select_hkl( new_context, GetKeyboardLayout( 0 ) ); - TRACE("Created context %p\n", new_context); return new_context; } @@ -934,14 +938,14 @@ static struct imc *default_input_context(void) static HWND get_ime_ui_window(void) { struct imc *imc = default_input_context(); + struct ime *ime; - imc_select_hkl( imc, GetKeyboardLayout( 0 ) ); - if (!imc->ime) return 0; + if (!(ime = imc_select_ime( imc ))) return 0; if (!imc->ui_hwnd) { - imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, imc->ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, - ImmGetDefaultIMEWnd( 0 ), 0, imc->ime->module, 0 ); + imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, + ImmGetDefaultIMEWnd( 0 ), 0, ime->module, 0 ); SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)imc->handle ); } return imc->ui_hwnd; @@ -1153,6 +1157,7 @@ DWORD WINAPI ImmGetCandidateListA( struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; LPCANDIDATELIST candlist; + struct ime *ime; DWORD ret = 0; TRACE("%p, %ld, %p, %ld\n", hIMC, dwIndex, lpCandList, dwBufLen); @@ -1168,7 +1173,9 @@ DWORD WINAPI ImmGetCandidateListA( if ( !candlist->dwSize || !candlist->dwCount ) goto done; - if (!ime_is_unicode( data->ime )) + if (!(ime = imc_select_ime( data ))) + ret = 0; + else if (!ime_is_unicode( ime )) { ret = candlist->dwSize; if ( lpCandList && dwBufLen >= ret ) @@ -1191,6 +1198,7 @@ DWORD WINAPI ImmGetCandidateListCountA( struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; DWORD ret, count; + struct ime *ime; TRACE("%p, %p\n", hIMC, lpdwListCount); @@ -1201,7 +1209,9 @@ DWORD WINAPI ImmGetCandidateListCountA( *lpdwListCount = count = candinfo->dwCount; - if (!ime_is_unicode( data->ime )) + if (!(ime = imc_select_ime( data ))) + ret = 0; + else if (!ime_is_unicode( ime )) ret = candinfo->dwSize; else { @@ -1223,6 +1233,7 @@ DWORD WINAPI ImmGetCandidateListCountW( struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; DWORD ret, count; + struct ime *ime; TRACE("%p, %p\n", hIMC, lpdwListCount); @@ -1233,7 +1244,9 @@ DWORD WINAPI ImmGetCandidateListCountW( *lpdwListCount = count = candinfo->dwCount; - if (ime_is_unicode( data->ime )) + if (!(ime = imc_select_ime( data ))) + ret = 0; + else if (ime_is_unicode( ime )) ret = candinfo->dwSize; else { @@ -1256,6 +1269,7 @@ DWORD WINAPI ImmGetCandidateListW( struct imc *data = get_imc_data( hIMC ); LPCANDIDATEINFO candinfo; LPCANDIDATELIST candlist; + struct ime *ime; DWORD ret = 0; TRACE("%p, %ld, %p, %ld\n", hIMC, dwIndex, lpCandList, dwBufLen); @@ -1271,7 +1285,9 @@ DWORD WINAPI ImmGetCandidateListW( if ( !candlist->dwSize || !candlist->dwCount ) goto done; - if (ime_is_unicode( data->ime )) + if (!(ime = imc_select_ime( data ))) + ret = 0; + else if (ime_is_unicode( ime )) { ret = candlist->dwSize; if ( lpCandList && dwBufLen >= ret ) @@ -1351,15 +1367,15 @@ BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) /* Source encoding is defined by context, source length is always given in respective characters. Destination buffer length is always in bytes. */ -static INT CopyCompStringIMEtoClient( const struct imc *data, const void *src, INT src_len, - void *dst, INT dst_len, BOOL unicode ) +static INT CopyCompStringIMEtoClient( BOOL src_unicode, const void *src, INT src_len, + void *dst, INT dst_len, BOOL dst_unicode ) { - int char_size = unicode ? sizeof(WCHAR) : sizeof(char); + int char_size = dst_unicode ? sizeof(WCHAR) : sizeof(char); INT ret; - if (ime_is_unicode( data->ime ) ^ unicode) + if (src_unicode ^ dst_unicode) { - if (unicode) + if (dst_unicode) ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR)); else ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL); @@ -1381,8 +1397,8 @@ static INT CopyCompStringIMEtoClient( const struct imc *data, const void *src, I /* Composition string encoding is defined by context, returned attributes correspond to string, converted according to passed mode. String length is in characters, attributes are in byte arrays. */ -static INT CopyCompAttrIMEtoClient( const struct imc *data, const BYTE *src, INT src_len, const void *comp_string, - INT str_len, BYTE *dst, INT dst_len, BOOL unicode ) +static INT CopyCompAttrIMEtoClient( BOOL src_unicode, const BYTE *src, INT src_len, const void *comp_string, INT str_len, + BYTE *dst, INT dst_len, BOOL unicode ) { union { @@ -1394,7 +1410,7 @@ static INT CopyCompAttrIMEtoClient( const struct imc *data, const BYTE *src, INT string.str = comp_string; - if (ime_is_unicode( data->ime ) && !unicode) + if (src_unicode && !unicode) { rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL); if (dst_len) @@ -1421,7 +1437,7 @@ static INT CopyCompAttrIMEtoClient( const struct imc *data, const BYTE *src, INT rc = j; } } - else if (!ime_is_unicode( data->ime ) && unicode) + else if (!src_unicode && unicode) { rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0); if (dst_len) @@ -1452,12 +1468,12 @@ static INT CopyCompAttrIMEtoClient( const struct imc *data, const BYTE *src, INT return rc; } -static INT CopyCompClauseIMEtoClient( struct imc *data, LPBYTE source, INT slen, LPBYTE ssource, +static INT CopyCompClauseIMEtoClient( BOOL src_unicode, LPBYTE source, INT slen, LPBYTE ssource, LPBYTE target, INT tlen, BOOL unicode ) { INT rc; - if (ime_is_unicode( data->ime ) && !unicode) + if (src_unicode && !unicode) { if (tlen) { @@ -1478,7 +1494,7 @@ static INT CopyCompClauseIMEtoClient( struct imc *data, LPBYTE source, INT slen, else rc = slen; } - else if (!ime_is_unicode( data->ime ) && unicode) + else if (!src_unicode && unicode) { if (tlen) { @@ -1507,15 +1523,15 @@ static INT CopyCompClauseIMEtoClient( struct imc *data, LPBYTE source, INT slen, return rc; } -static INT CopyCompOffsetIMEtoClient( struct imc *data, DWORD offset, LPBYTE ssource, BOOL unicode ) +static INT CopyCompOffsetIMEtoClient( BOOL src_unicode, DWORD offset, LPBYTE ssource, BOOL unicode ) { int rc; - if (ime_is_unicode( data->ime ) && !unicode) + if (src_unicode && !unicode) { rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL); } - else if (!ime_is_unicode( data->ime ) && unicode) + else if (!src_unicode && unicode) { rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0); } @@ -1531,6 +1547,8 @@ static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, LONG rc = 0; struct imc *data = get_imc_data( hIMC ); LPCOMPOSITIONSTRING compstr; + BOOL src_unicode; + struct ime *ime; LPBYTE compdata; TRACE("(%p, 0x%lx, %p, %ld)\n", hIMC, dwIndex, lpBuf, dwBufLen); @@ -1541,6 +1559,10 @@ static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, if (!data->IMC.hCompStr) return FALSE; + if (!(ime = imc_select_ime( data ))) + return FALSE; + src_unicode = ime_is_unicode( ime ); + compdata = ImmLockIMCC(data->IMC.hCompStr); compstr = (LPCOMPOSITIONSTRING)compdata; @@ -1548,63 +1570,63 @@ static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, { case GCS_RESULTSTR: TRACE("GCS_RESULTSTR\n"); - rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode); + rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPSTR: TRACE("GCS_COMPSTR\n"); - rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode); + rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPATTR: TRACE("GCS_COMPATTR\n"); - rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen, + rc = CopyCompAttrIMEtoClient(src_unicode, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPCLAUSE: TRACE("GCS_COMPCLAUSE\n"); - rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen, + rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen, compdata + compstr->dwCompStrOffset, lpBuf, dwBufLen, unicode); break; case GCS_RESULTCLAUSE: TRACE("GCS_RESULTCLAUSE\n"); - rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen, + rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen, compdata + compstr->dwResultStrOffset, lpBuf, dwBufLen, unicode); break; case GCS_RESULTREADSTR: TRACE("GCS_RESULTREADSTR\n"); - rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode); + rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode); break; case GCS_RESULTREADCLAUSE: TRACE("GCS_RESULTREADCLAUSE\n"); - rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen, + rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen, compdata + compstr->dwResultStrOffset, lpBuf, dwBufLen, unicode); break; case GCS_COMPREADSTR: TRACE("GCS_COMPREADSTR\n"); - rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode); + rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPREADATTR: TRACE("GCS_COMPREADATTR\n"); - rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen, + rc = CopyCompAttrIMEtoClient(src_unicode, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPREADCLAUSE: TRACE("GCS_COMPREADCLAUSE\n"); - rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen, + rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen, compdata + compstr->dwCompStrOffset, lpBuf, dwBufLen, unicode); break; case GCS_CURSORPOS: TRACE("GCS_CURSORPOS\n"); - rc = CopyCompOffsetIMEtoClient(data, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode); + rc = CopyCompOffsetIMEtoClient(src_unicode, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode); break; case GCS_DELTASTART: TRACE("GCS_DELTASTART\n"); - rc = CopyCompOffsetIMEtoClient(data, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode); + rc = CopyCompOffsetIMEtoClient(src_unicode, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode); break; default: FIXME("Unhandled index 0x%lx\n",dwIndex); @@ -2198,6 +2220,7 @@ BOOL WINAPI ImmNotifyIME( HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) { struct imc *data = get_imc_data( hIMC ); + struct ime *ime; TRACE("(%p, %ld, %ld, %ld)\n", hIMC, dwAction, dwIndex, dwValue); @@ -2213,7 +2236,8 @@ BOOL WINAPI ImmNotifyIME( return FALSE; } - return data->ime->pNotifyIME( hIMC, dwAction, dwIndex, dwValue ); + if (!(ime = imc_select_ime( data ))) return FALSE; + return ime->pNotifyIME( hIMC, dwAction, dwIndex, dwValue ); } /*********************************************************************** @@ -2404,6 +2428,7 @@ BOOL WINAPI ImmSetCompositionStringA( WCHAR *ReadBuffer = NULL; BOOL rc; struct imc *data = get_imc_data( hIMC ); + struct ime *ime; TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); @@ -2420,8 +2445,8 @@ BOOL WINAPI ImmSetCompositionStringA( dwIndex == SCS_QUERYRECONVERTSTRING)) return FALSE; - if (!ime_is_unicode( data->ime )) - return data->ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); + if (!(ime = imc_select_ime( data ))) return FALSE; + if (!ime_is_unicode( ime )) return ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0); if (comp_len) @@ -2460,6 +2485,7 @@ BOOL WINAPI ImmSetCompositionStringW( CHAR *ReadBuffer = NULL; BOOL rc; struct imc *data = get_imc_data( hIMC ); + struct ime *ime; TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); @@ -2476,8 +2502,8 @@ BOOL WINAPI ImmSetCompositionStringW( dwIndex == SCS_QUERYRECONVERTSTRING)) return FALSE; - if (ime_is_unicode( data->ime )) - return data->ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); + if (!(ime = imc_select_ime( data ))) return FALSE; + if (ime_is_unicode( ime )) return ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen ); comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL, NULL); @@ -2738,6 +2764,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE IMEMENUITEMINFOA *menuA, DWORD size ) { struct imc *data = get_imc_data( himc ); + struct ime *ime; DWORD ret; TRACE( "himc %p, flags %#lx, type %lu, parentA %p, menuA %p, size %lu.\n", @@ -2749,8 +2776,9 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (!ime_is_unicode( data->ime ) || (!parentA && !menuA)) - ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); + if (!(ime = imc_select_ime( data ))) return 0; + if (!ime_is_unicode( ime ) || (!parentA && !menuA)) + ret = ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); else { IMEMENUITEMINFOW tmpW, *menuW, *parentW = parentA ? &tmpW : NULL; @@ -2763,7 +2791,7 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITE menuW = malloc( size ); } - ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); + ret = ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); if (parentA) { @@ -2796,6 +2824,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE IMEMENUITEMINFOW *menuW, DWORD size ) { struct imc *data = get_imc_data( himc ); + struct ime *ime; DWORD ret; TRACE( "himc %p, flags %#lx, type %lu, parentW %p, menuW %p, size %lu.\n", @@ -2807,8 +2836,9 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE return 0; } - if (ime_is_unicode( data->ime ) || (!parentW && !menuW)) - ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); + if (!(ime = imc_select_ime( data ))) return 0; + if (ime_is_unicode( ime ) || (!parentW && !menuW)) + ret = ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size ); else { IMEMENUITEMINFOA tmpA, *menuA, *parentA = parentW ? &tmpA : NULL; @@ -2821,7 +2851,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITE menuA = malloc( size ); } - ret = data->ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); + ret = ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size ); if (parentW) { @@ -2857,7 +2887,7 @@ INPUTCONTEXT *WINAPI ImmLockIMC( HIMC himc ) if (!imc) return NULL; imc->dwLock++; - imc_select_hkl( imc, GetKeyboardLayout( 0 ) ); + imc_select_ime( imc ); return &imc->IMC; } @@ -2988,6 +3018,7 @@ BOOL WINAPI ImmGenerateMessage(HIMC hIMC) BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData) { struct imc *data; + struct ime *ime; HIMC imc = ImmGetContext(hwnd); BYTE state[256]; UINT scancode; @@ -2999,6 +3030,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD TRACE("%p %x %x %x\n",hwnd, msg, (UINT)wParam, (UINT)lKeyData); if (!(data = get_imc_data( imc ))) return FALSE; + if (!(ime = imc_select_ime( imc ))) return FALSE; if (data->lastVK == VK_PROCESSKEY) return FALSE; GetKeyboardState(state); @@ -3007,11 +3039,11 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD list = calloc( list_count, sizeof(TRANSMSG) + sizeof(DWORD) ); list->uMsgCount = list_count; - if (data->ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) + if (ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { WCHAR chr; - if (!ime_is_unicode( data->ime )) + if (!ime_is_unicode( ime )) ToAscii( data->lastVK, scancode, state, &chr, 0 ); else ToUnicodeEx(data->lastVK, scancode, state, &chr, 1, 0, GetKeyboardLayout(0)); @@ -3020,7 +3052,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD else uVirtKey = data->lastVK; - msg_count = data->ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, imc ); + msg_count = ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, imc ); TRACE("%i messages generated\n",msg_count); if (msg_count && msg_count <= list_count) { @@ -3047,6 +3079,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM lparam, DWORD unknown ) { struct imc *imc; + struct ime *ime; BYTE state[256]; BOOL ret; @@ -3054,12 +3087,11 @@ BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM lparam, DWORD u if (hkl != GetKeyboardLayout( 0 )) return FALSE; if (!(imc = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; - imc_select_hkl( imc, hkl ); - if (!imc->ime) return FALSE; + if (!(ime = imc_select_ime( imc ))) return FALSE; GetKeyboardState( state ); - ret = imc->ime->pImeProcessKey( imc->handle, vkey, lparam, state ); + ret = ime->pImeProcessKey( imc->handle, vkey, lparam, state ); imc->lastVK = ret ? vkey : VK_PROCESSKEY; return ret; diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 4d9d213cb90..9a0594038c9 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3931,7 +3931,6 @@ static void test_ImmProcessKey(void) ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) ); - todo_wine ok_seq( empty_sequence ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -4227,7 +4226,6 @@ static void test_ImmCreateInputContext(void) { .hkl = expect_ime, .himc = 0/*himc[1]*/, .func = IME_SELECT, .select = 1, - .todo = TRUE, }, {0}, }; @@ -4336,7 +4334,6 @@ static void test_ImmCreateInputContext(void) himc[1] = ImmCreateContext(); ok( !!himc[1], "ImmCreateContext failed, error %lu\n", GetLastError() ); - todo_wine ok_seq( empty_sequence ); ctx = ImmLockIMC( himc[1] ); ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); From 6f3d63f03382b4b1487d154d03b5907f4be8ed06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Mar 2023 13:06:00 +0100 Subject: [PATCH 178/758] imm32: Introduce new input_context_init helper. (cherry picked from commit f24479793bf09f5f1820b8091d2341fc9d82d937) --- dlls/imm32/imm.c | 77 ++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index b2650fcfeb9..b8feac86e94 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -637,6 +637,50 @@ static BOOL free_input_context_data( HIMC hIMC ) return TRUE; } +static void input_context_init( INPUTCONTEXT *ctx ) +{ + COMPOSITIONSTRING *str; + CANDIDATEINFO *info; + GUIDELINE *line; + UINT i; + + if (!(ctx->hMsgBuf = ImmCreateIMCC( 0 ))) + WARN( "Failed to allocate %p message buffer\n", ctx ); + + if (!(ctx->hCompStr = ImmCreateIMCC( sizeof(COMPOSITIONSTRING) ))) + WARN( "Failed to allocate %p COMPOSITIONSTRING\n", ctx ); + else if (!(str = ImmLockIMCC( ctx->hCompStr ))) + WARN( "Failed to lock IMCC for COMPOSITIONSTRING\n" ); + else + { + str->dwSize = sizeof(COMPOSITIONSTRING); + ImmUnlockIMCC( ctx->hCompStr ); + } + + if (!(ctx->hCandInfo = ImmCreateIMCC( sizeof(CANDIDATEINFO) ))) + WARN( "Failed to allocate %p CANDIDATEINFO\n", ctx ); + else if (!(info = ImmLockIMCC( ctx->hCandInfo ))) + WARN( "Failed to lock IMCC for CANDIDATEINFO\n" ); + else + { + info->dwSize = sizeof(CANDIDATEINFO); + ImmUnlockIMCC( ctx->hCandInfo ); + } + + if (!(ctx->hGuideLine = ImmCreateIMCC( sizeof(GUIDELINE) ))) + WARN( "Failed to allocate %p GUIDELINE\n", ctx ); + else if (!(line = ImmLockIMCC( ctx->hGuideLine ))) + WARN( "Failed to lock IMCC for GUIDELINE\n" ); + else + { + line->dwSize = sizeof(GUIDELINE); + ImmUnlockIMCC( ctx->hGuideLine ); + } + + for (i = 0; i < ARRAY_SIZE(ctx->cfCandForm); i++) + ctx->cfCandForm[i].dwIndex = ~0u; +} + static void IMM_FreeThreadData(void) { struct coinit_spy *spy; @@ -718,18 +762,6 @@ static LRESULT ImmInternalSendIMENotify( struct imc *data, WPARAM notify, LPARAM return 0; } -static HIMCC ImmCreateBlankCompStr(void) -{ - HIMCC rc; - LPCOMPOSITIONSTRING ptr; - rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); - ptr = ImmLockIMCC(rc); - memset(ptr,0,sizeof(COMPOSITIONSTRING)); - ptr->dwSize = sizeof(COMPOSITIONSTRING); - ImmUnlockIMCC(rc); - return rc; -} - /*********************************************************************** * ImmSetActiveContext (IMM32.@) */ @@ -883,28 +915,9 @@ BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data ) static struct imc *create_input_context( HIMC default_imc ) { struct imc *new_context; - LPGUIDELINE gl; - LPCANDIDATEINFO ci; - int i; if (!(new_context = calloc( 1, sizeof(*new_context) ))) return NULL; - - /* the HIMCCs are never NULL */ - new_context->IMC.hCompStr = ImmCreateBlankCompStr(); - new_context->IMC.hMsgBuf = ImmCreateIMCC(0); - new_context->IMC.hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO)); - ci = ImmLockIMCC(new_context->IMC.hCandInfo); - memset(ci,0,sizeof(CANDIDATEINFO)); - ci->dwSize = sizeof(CANDIDATEINFO); - ImmUnlockIMCC(new_context->IMC.hCandInfo); - new_context->IMC.hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE)); - gl = ImmLockIMCC(new_context->IMC.hGuideLine); - memset(gl,0,sizeof(GUIDELINE)); - gl->dwSize = sizeof(GUIDELINE); - ImmUnlockIMCC(new_context->IMC.hGuideLine); - - for (i = 0; i < ARRAY_SIZE(new_context->IMC.cfCandForm); i++) - new_context->IMC.cfCandForm[i].dwIndex = ~0u; + input_context_init( &new_context->IMC ); if (!default_imc) new_context->handle = NtUserCreateInputContext((UINT_PTR)new_context); From c9f412ca6141907ca471446cd0c2a4eaeaf5cb7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 6 Mar 2023 22:58:36 +0100 Subject: [PATCH 179/758] imm32: Add a default implementation for IME functions. To be used by graphics drivers. (cherry picked from commit 3d694c8118a848fb7df27200017e42a0efc11db8) --- dlls/imm32/Makefile.in | 1 + dlls/imm32/ime.c | 151 +++++++++++++++++++++++++++++++++++++++++ dlls/imm32/imm.c | 13 ++-- 3 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 dlls/imm32/ime.c diff --git a/dlls/imm32/Makefile.in b/dlls/imm32/Makefile.in index b4e3039849e..baf10c5f1dc 100644 --- a/dlls/imm32/Makefile.in +++ b/dlls/imm32/Makefile.in @@ -4,6 +4,7 @@ IMPORTS = user32 gdi32 advapi32 kernelbase win32u DELAYIMPORTS = ole32 C_SRCS = \ + ime.c \ imm.c RC_SRCS = version.rc diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c new file mode 100644 index 00000000000..c31fd936ca1 --- /dev/null +++ b/dlls/imm32/ime.c @@ -0,0 +1,151 @@ +/* + * Copyright 2023 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 +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "imm.h" +#include "immdev.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(imm); + +BOOL WINAPI ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) +{ + FIXME( "info %p, ui_class %p, flags %#lx stub!\n", info, ui_class, flags ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +BOOL WINAPI ImeDestroy( UINT force ) +{ + FIXME( "force %u stub!\n", force ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) +{ + FIXME( "himc %p, select %d stub!\n", himc, select ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag ) +{ + FIXME( "himc %p, flag %#x stub!\n", himc, flag ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) +{ + FIXME( "himc %p, vkey %u, key_data %#Ix, key_state %p stub!\n", himc, vkey, key_data, key_state ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +UINT WINAPI ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) +{ + FIXME( "vkey %u, scan_code %u, key_state %p, msgs %p, state %u, himc %p stub!\n", + vkey, scan_code, key_state, msgs, state, himc ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + +BOOL WINAPI ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) +{ + FIXME( "hkl %p, hwnd %p, mode %lu, data %p stub!\n", hkl, hwnd, mode, data ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +DWORD WINAPI ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *dest, DWORD dest_len, UINT flag ) +{ + FIXME( "himc %p, source %s, dest %p, dest_len %lu, flag %#x stub!\n", + himc, debugstr_w(source), dest, dest_len, flag ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + +BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len, + const void *read, DWORD read_len ) +{ + FIXME( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu stub!\n", + himc, index, comp, comp_len, read, read_len ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) +{ + FIXME( "himc %p, action %lu, index %lu, value %lu stub!\n", himc, action, index, value ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +LRESULT WINAPI ImeEscape( HIMC himc, UINT escape, void *data ) +{ + FIXME( "himc %p, escape %#x, data %p stub!\n", himc, escape, data ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + +DWORD WINAPI ImeGetImeMenuItems( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parent, + IMEMENUITEMINFOW *menu, DWORD size ) +{ + FIXME( "himc %p, flags %#lx, type %lu, parent %p, menu %p, size %#lx stub!\n", + himc, flags, type, parent, menu, size ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + +BOOL WINAPI ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) +{ + FIXME( "reading %s, style %lu, string %s stub!\n", debugstr_w(reading), style, debugstr_w(string) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +UINT WINAPI ImeGetRegisterWordStyle( UINT item, STYLEBUFW *style ) +{ + FIXME( "item %u, style %p stub!\n", item, style ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + +BOOL WINAPI ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) +{ + FIXME( "reading %s, style %lu, string %s stub!\n", debugstr_w(reading), style, debugstr_w(string) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + +UINT WINAPI ImeEnumRegisterWord( REGISTERWORDENUMPROCW proc, const WCHAR *reading, DWORD style, + const WCHAR *string, void *data ) +{ + FIXME( "proc %p, reading %s, style %lu, string %s, data %p stub!\n", + proc, debugstr_w(reading), style, debugstr_w(string), data ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index b8feac86e94..e90279a4688 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -467,6 +467,7 @@ BOOL WINAPI ImmFreeLayout( HKL hkl ) BOOL WINAPI ImmLoadIME( HKL hkl ) { WCHAR buffer[MAX_PATH] = {0}; + BOOL use_default_ime; struct ime *ime; TRACE( "hkl %p\n", hkl ); @@ -479,17 +480,19 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) if (!(ime = calloc( 1, sizeof(*ime) ))) return FALSE; ime->hkl = hkl; - if (!ImmGetIMEFileNameW( hkl, buffer, MAX_PATH )) ime->module = NULL; - else ime->module = LoadLibraryW( buffer ); + if (!ImmGetIMEFileNameW( hkl, buffer, MAX_PATH )) use_default_ime = TRUE; + else if (!(ime->module = LoadLibraryW( buffer ))) use_default_ime = TRUE; + else use_default_ime = FALSE; - if (!ime->module) + if (use_default_ime) { if (*buffer) WARN( "Failed to load %s, falling back to default.\n", debugstr_w(buffer) ); - if (!(ime->module = load_graphics_driver())) goto failed; + if (!(ime->module = load_graphics_driver())) ime->module = LoadLibraryW( L"imm32" ); } #define LOAD_FUNCPTR( f ) \ - if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f ))) \ + if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f )) && \ + !(ime->p##f = use_default_ime ? (void *)f : NULL)) \ { \ WARN( "Can't find function %s in HKL %p IME\n", #f, hkl ); \ goto failed; \ From 9b404b8e1feb52d3614035963167ca32f3e1b786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 19 Feb 2023 12:01:59 +0100 Subject: [PATCH 180/758] imm32: Return TRUE from ImmIsIME with any HKL. (cherry picked from commit 32c5b57ac80673c2bad7291d27b4001aac31ae9b) --- dlls/imm32/imm.c | 7 +------ dlls/imm32/tests/imm32.c | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e90279a4688..5cdf14cc9d9 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2175,13 +2175,8 @@ HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description ) */ BOOL WINAPI ImmIsIME( HKL hkl ) { - struct ime *ime; - TRACE( "hkl %p\n", hkl ); - - if (!(ime = ime_acquire( hkl ))) return 0; - ime_release( ime ); - + if (!hkl) return FALSE; return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 9a0594038c9..3892eccb147 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3238,7 +3238,6 @@ static void test_ImmIsIME(void) SET_ENABLE( IME_DLL_PROCESS_DETACH, TRUE ); SetLastError( 0xdeadbeef ); - todo_wine ok_ret( 0, ImmIsIME( 0 ) ); ok_ret( 0xdeadbeef, GetLastError() ); ok_ret( 1, ImmIsIME( hkl ) ); From 96b21368e2a0dbbdc19566b6d98faf2132605732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 7 Mar 2023 23:10:05 +0100 Subject: [PATCH 181/758] winex11: Use the default IME implementation for stubs. (cherry picked from commit 2a4dff01bd2dec7129e7b572dbb8e29ba75723df) --- dlls/imm32/ime.c | 11 ++--- dlls/imm32/tests/imm32.c | 1 + dlls/winex11.drv/ime.c | 79 ------------------------------- dlls/winex11.drv/winex11.drv.spec | 9 ---- 4 files changed, 6 insertions(+), 94 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index c31fd936ca1..c05ca6d0255 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -46,16 +46,15 @@ BOOL WINAPI ImeDestroy( UINT force ) BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) { - FIXME( "himc %p, select %d stub!\n", himc, select ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + FIXME( "himc %p, select %d semi-stub!\n", himc, select ); + return TRUE; } BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag ) { - FIXME( "himc %p, flag %#x stub!\n", himc, flag ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + static int once; + if (!once++) FIXME( "himc %p, flag %#x stub!\n", himc, flag ); + return TRUE; } BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 3892eccb147..3a75d9636e7 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3324,6 +3324,7 @@ static void test_ImmGetProperty(void) ok_ret( expect->fdwSelectCaps, ImmGetProperty( hkl, IGP_SELECT ) ); ok_ret( IMEVER_0400, ImmGetProperty( hkl, IGP_GETIMEVERSION ) ); ok_ret( expect->fdwUICaps, ImmGetProperty( hkl, IGP_UI ) ); + todo_wine ok_ret( 0xdeadbeef, GetLastError() ); /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index a293daa6ad9..9c0894717ad 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -525,23 +525,6 @@ BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, DWORD flags) return TRUE; } -BOOL WINAPI ImeConfigure(HKL hKL,HWND hWnd, DWORD dwMode, LPVOID lpData) -{ - FIXME("(%p, %p, %ld, %p): stub\n", hKL, hWnd, dwMode, lpData); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSource, - LPCANDIDATELIST lpCandList, DWORD dwBufLen, UINT uFlag) - -{ - FIXME("(%p, %s, %p, %ld, %d): stub\n", hIMC, debugstr_w(lpSource), - lpCandList, dwBufLen, uFlag); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - BOOL WINAPI ImeDestroy(UINT uForce) { TRACE("\n"); @@ -551,13 +534,6 @@ BOOL WINAPI ImeDestroy(UINT uForce) return TRUE; } -LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData) -{ - FIXME("(%p, %d, %p): stub\n", hIMC, uSubFunc, lpData); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) { /* See the comment at the head of this file */ @@ -602,15 +578,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } -BOOL WINAPI ImeSetActiveContext(HIMC hIMC,BOOL fFlag) -{ - static int once; - - if (!once++) - FIXME("(%p, %x): stub\n", hIMC, fFlag); - return TRUE; -} - UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC) { @@ -777,42 +744,6 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) return bRet; } -BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle, - LPCWSTR lpszRegister) -{ - FIXME("(%s, %ld, %s): stub\n", debugstr_w(lpszReading), dwStyle, - debugstr_w(lpszRegister)); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle, - LPCWSTR lpszUnregister) -{ - FIXME("(%s, %ld, %s): stub\n", debugstr_w(lpszReading), dwStyle, - debugstr_w(lpszUnregister)); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf) -{ - FIXME("(%d, %p): stub\n", nItem, lpStyleBuf); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - -UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc, - LPCWSTR lpszReading, DWORD dwStyle, - LPCWSTR lpszRegister, LPVOID lpData) -{ - FIXME("(%p, %s, %ld, %s, %p): stub\n", lpfnEnumProc, - debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister), - lpData); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, LPCVOID lpRead, DWORD dwReadLen) @@ -888,16 +819,6 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, return TRUE; } -DWORD WINAPI ImeGetImeMenuItems(HIMC hIMC, DWORD dwFlags, DWORD dwType, - LPIMEMENUITEMINFOW lpImeParentMenu, LPIMEMENUITEMINFOW lpImeMenu, - DWORD dwSize) -{ - FIXME("(%p, %lx %lx %p %p %lx): stub\n", hIMC, dwFlags, dwType, - lpImeParentMenu, lpImeMenu, dwSize); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - /* Interfaces to XIM and other parts of winex11drv */ NTSTATUS x11drv_ime_set_open_status( UINT open ) diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index 77e4a6285de..0596d48c577 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -12,18 +12,9 @@ #IME Interface @ stdcall ImeInquire(ptr ptr wstr) -@ stdcall ImeConfigure(long long long ptr) @ stdcall ImeDestroy(long) -@ stdcall ImeEscape(long long ptr) @ stdcall ImeSelect(long long) -@ stdcall ImeSetActiveContext(long long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) @ stdcall NotifyIME(long long long long) -@ stdcall ImeRegisterWord(wstr long wstr) -@ stdcall ImeUnregisterWord(wstr long wstr) -@ stdcall ImeEnumRegisterWord(ptr wstr long wstr ptr) @ stdcall ImeSetCompositionString(long long ptr long ptr long) -@ stdcall ImeConversionList(long wstr ptr long long) @ stdcall ImeProcessKey(long long long ptr) -@ stdcall ImeGetRegisterWordStyle(long ptr) -@ stdcall ImeGetImeMenuItems(long long long ptr ptr long) From aa4b959c9d5d39ee569488a83cc9134c294a3186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 7 Mar 2023 22:57:51 +0100 Subject: [PATCH 182/758] winemac: Use the default IME implementation for stubs. (cherry picked from commit 50352739cafc76e2ca27b989aaede072d5f6494d) --- dlls/winemac.drv/ime.c | 70 ------------------------------- dlls/winemac.drv/winemac.drv.spec | 9 ---- 2 files changed, 79 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 4bdfcbc6730..f779b4d06b0 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -520,23 +520,6 @@ static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) UnlockRealIMC(hIMC); } -BOOL WINAPI ImeConfigure(HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) -{ - FIXME("(%p, %p, %ld, %p): stub\n", hKL, hWnd, dwMode, lpData); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSource, LPCANDIDATELIST lpCandList, - DWORD dwBufLen, UINT uFlag) - -{ - FIXME("(%p, %s, %p, %ld, %d): stub\n", hIMC, debugstr_w(lpSource), lpCandList, - dwBufLen, uFlag); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - BOOL WINAPI ImeDestroy(UINT uForce) { TRACE("\n"); @@ -546,12 +529,6 @@ BOOL WINAPI ImeDestroy(UINT uForce) return TRUE; } -LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData) -{ - TRACE("%x %p\n", uSubFunc, lpData); - return 0; -} - BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) { LPINPUTCONTEXT lpIMC; @@ -637,15 +614,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } -BOOL WINAPI ImeSetActiveContext(HIMC hIMC, BOOL fFlag) -{ - static int once; - - if (!once++) - FIXME("(%p, %x): stub\n", hIMC, fFlag); - return TRUE; -} - UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC) { @@ -870,36 +838,6 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) return bRet; } -BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister) -{ - FIXME("(%s, %ld, %s): stub\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister)); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister) -{ - FIXME("(%s, %ld, %s): stub\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszUnregister)); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf) -{ - FIXME("(%d, %p): stub\n", nItem, lpStyleBuf); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - -UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc, LPCWSTR lpszReading, - DWORD dwStyle, LPCWSTR lpszRegister, LPVOID lpData) -{ - FIXME("(%p, %s, %ld, %s, %p): stub\n", lpfnEnumProc, debugstr_w(lpszReading), dwStyle, - debugstr_w(lpszRegister), lpData); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid) { LPINPUTCONTEXT lpIMC; @@ -987,14 +925,6 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DW return IME_SetCompositionString(hIMC, dwIndex, lpComp, dwCompLen, 0, FALSE); } -DWORD WINAPI ImeGetImeMenuItems(HIMC hIMC, DWORD dwFlags, DWORD dwType, LPIMEMENUITEMINFOW lpImeParentMenu, - LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize) -{ - FIXME("(%p, %lx %lx %p %p %lx): stub\n", hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; -} - static void IME_NotifyComplete(void* hIMC) { NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index b060d1cc2a6..d5e94b53ce4 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -2,19 +2,10 @@ @ cdecl wine_notify_icon(long ptr) # IME -@ stdcall ImeConfigure(long long long ptr) -@ stdcall ImeConversionList(long wstr ptr long long) @ stdcall ImeDestroy(long) -@ stdcall ImeEnumRegisterWord(ptr wstr long wstr ptr) -@ stdcall ImeEscape(long long ptr) -@ stdcall ImeGetImeMenuItems(long long long ptr ptr long) -@ stdcall ImeGetRegisterWordStyle(long ptr) @ stdcall ImeInquire(ptr wstr wstr) @ stdcall ImeProcessKey(long long long ptr) -@ stdcall ImeRegisterWord(wstr long wstr) @ stdcall ImeSelect(long long) -@ stdcall ImeSetActiveContext(long long) @ stdcall ImeSetCompositionString(long long ptr long ptr long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) -@ stdcall ImeUnregisterWord(wstr long wstr) @ stdcall NotifyIME(long long long long) From e8e9020f713fbae084b0fe40b443dac06e441edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 08:55:57 +0200 Subject: [PATCH 183/758] imm32/tests: Add some ImmSetConversionStatus tests. (cherry picked from commit 13f0b5c9c6e9901ebf98a284219dd15790d620da) --- dlls/imm32/tests/imm32.c | 165 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 3a75d9636e7..772f3183679 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3890,6 +3890,168 @@ static void test_ImmUnregisterWord( BOOL unicode ) winetest_pop_context(); } +static void test_ImmSetConversionStatus(void) +{ + const struct ime_call set_conversion_status_0_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSENTENCEMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, + }, + {0}, + }; + const struct ime_call set_conversion_status_1_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xdeadbeef, .value = IMC_SETCONVERSIONMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + }, + {0}, + }; + const struct ime_call set_conversion_status_2_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xfeedcafe, .value = IMC_SETSENTENCEMODE}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, + }, + {0}, + }; + DWORD old_conversion, old_sentence, conversion, sentence; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + INPUTCONTEXT *ctx; + + ok_ret( 0, ImmGetConversionStatus( 0, &old_conversion, &old_sentence ) ); + ok_ret( 1, ImmGetConversionStatus( default_himc, &old_conversion, &old_sentence ) ); + + ctx = ImmLockIMC( default_himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + process_messages(); + + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( old_conversion, conversion, UINT, "%#x" ); + ok_eq( old_sentence, sentence, UINT, "%#x" ); + ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); + + ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) ); + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( 0, conversion, UINT, "%#x" ); + ok_eq( 0, sentence, UINT, "%#x" ); + ok_eq( 0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( 0, ctx->fdwSentence, UINT, "%#x" ); + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + process_messages(); + /* initial values are dependent on both old and new IME */ + ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( 0, conversion, UINT, "%#x" ); + ok_eq( 0, sentence, UINT, "%#x" ); + ok_eq( 0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( 0, ctx->fdwSentence, UINT, "%#x" ); + + ok_seq( empty_sequence ); + ok_ret( 1, ImmSetConversionStatus( default_himc, 0xdeadbeef, 0xfeedcafe ) ); + ok_seq( set_conversion_status_0_seq ); + + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( 0xdeadbeef, conversion, UINT, "%#x" ); + ok_eq( 0xfeedcafe, sentence, UINT, "%#x" ); + ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" ); + + ok_seq( empty_sequence ); + ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0xfeedcafe ) ); + ok_seq( set_conversion_status_1_seq ); + + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( 0, conversion, UINT, "%#x" ); + ok_eq( 0xfeedcafe, sentence, UINT, "%#x" ); + ok_eq( 0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" ); + + ok_seq( empty_sequence ); + ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) ); + ok_seq( set_conversion_status_2_seq ); + + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( ~0, conversion, UINT, "%#x" ); + ok_eq( ~0, sentence, UINT, "%#x" ); + ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); + + /* status is cached and some bits are kept from the previous active IME */ + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); + ok_ret( 1, ImmActivateLayout( hkl ) ); + todo_wine ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" ); + todo_wine ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); + + ime_cleanup( hkl, TRUE ); + +cleanup: + /* sanitize conversion status to some sane default */ + ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0 ) ); + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); + ok_eq( 0, conversion, UINT, "%#x" ); + ok_eq( 0, sentence, UINT, "%#x" ); + ok_eq( 0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( 0, ctx->fdwSentence, UINT, "%#x" ); + + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 1, ImmUnlockIMC( default_himc ) ); +} + static void test_ImmProcessKey(void) { const struct ime_call process_key_seq[] = @@ -4485,6 +4647,9 @@ START_TEST(imm32) test_ImmUnregisterWord( FALSE ); test_ImmUnregisterWord( TRUE ); + /* test these first to sanitize conversion / open statuses */ + test_ImmSetConversionStatus(); + test_ImmActivateLayout(); test_ImmCreateInputContext(); test_ImmProcessKey(); From c1e37efe21769126986ae9992813ee5eef34c407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 10:12:59 +0200 Subject: [PATCH 184/758] imm32/tests: Add some ImmSetOpenStatus tests. (cherry picked from commit feb427db1a59ed362797354eaabee4eb326de1b8) --- dlls/imm32/tests/imm32.c | 119 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 772f3183679..ae2b5b2a4f2 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4052,6 +4052,124 @@ static void test_ImmSetConversionStatus(void) ok_ret( 1, ImmUnlockIMC( default_himc ) ); } +static void test_ImmSetOpenStatus(void) +{ + const struct ime_call set_open_status_0_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + }, + {0}, + }; + const struct ime_call set_open_status_1_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + .todo = TRUE, + }, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + DWORD old_status, status; + INPUTCONTEXT *ctx; + + ok_ret( 0, ImmGetOpenStatus( 0 ) ); + old_status = ImmGetOpenStatus( default_himc ); + + ctx = ImmLockIMC( default_himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + process_messages(); + + status = ImmGetOpenStatus( default_himc ); + ok_eq( old_status, status, UINT, "%#x" ); + ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); + + ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) ); + status = ImmGetOpenStatus( default_himc ); + ok_eq( 0, status, UINT, "%#x" ); + ok_eq( 0, ctx->fOpen, UINT, "%#x" ); + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + process_messages(); + /* initial values are dependent on both old and new IME */ + ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + status = ImmGetOpenStatus( default_himc ); + ok_eq( 0, status, UINT, "%#x" ); + ok_eq( 0, ctx->fOpen, UINT, "%#x" ); + + ok_seq( empty_sequence ); + ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) ); + ok_seq( set_open_status_0_seq ); + + status = ImmGetOpenStatus( default_himc ); + ok_eq( 0xdeadbeef, status, UINT, "%#x" ); + ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + + ok_seq( empty_sequence ); + ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) ); + ok_seq( set_open_status_1_seq ); + + status = ImmGetOpenStatus( default_himc ); + todo_wine ok_eq( ~0, status, UINT, "%#x" ); + todo_wine ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); + + /* status is cached between IME activations */ + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + status = ImmGetOpenStatus( default_himc ); + todo_wine ok_eq( old_status, status, UINT, "%#x" ); + todo_wine ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); + ok_ret( 1, ImmActivateLayout( hkl ) ); + status = ImmGetOpenStatus( default_himc ); + todo_wine ok_eq( 1, status, UINT, "%#x" ); + todo_wine ok_eq( 1, ctx->fOpen, UINT, "%#x" ); + ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) ); + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + status = ImmGetOpenStatus( default_himc ); + ok_eq( old_status, status, UINT, "%#x" ); + ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); + + ime_cleanup( hkl, TRUE ); + +cleanup: + /* sanitize open status to some sane default */ + ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) ); + status = ImmGetOpenStatus( default_himc ); + ok_eq( 0, status, UINT, "%#x" ); + ok_eq( 0, ctx->fOpen, UINT, "%#x" ); + + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 1, ImmUnlockIMC( default_himc ) ); +} + static void test_ImmProcessKey(void) { const struct ime_call process_key_seq[] = @@ -4649,6 +4767,7 @@ START_TEST(imm32) /* test these first to sanitize conversion / open statuses */ test_ImmSetConversionStatus(); + test_ImmSetOpenStatus(); test_ImmActivateLayout(); test_ImmCreateInputContext(); From f78ebcdfdac8d6f44cf499f3e5cecf13ad0dd93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 11:32:16 +0200 Subject: [PATCH 185/758] imm32: Avoid recursing into ImeSelect calls. (cherry picked from commit ddfbc66fcfe5e6639352ec09cfffb753f5b6c675) --- dlls/imm32/imm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 5cdf14cc9d9..e165544ba5e 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -568,24 +568,25 @@ static void ime_release( struct ime *ime ) LeaveCriticalSection( &ime_cs ); } -static void imc_release_ime( struct imc *imc ) +static void imc_release_ime( struct imc *imc, struct ime *ime ) { if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd ); imc->ui_hwnd = NULL; - imc->ime->pImeSelect( imc->handle, FALSE ); - ime_release( imc->ime ); - imc->ime = NULL; + ime->pImeSelect( imc->handle, FALSE ); + ime_release( ime ); ImmDestroyIMCC( imc->IMC.hPrivate ); } static struct ime *imc_select_ime( struct imc *imc ) { HKL hkl = GetKeyboardLayout( 0 ); + struct ime *ime; - if (imc->ime) + if ((ime = imc->ime)) { - if (imc->ime->hkl == hkl) return imc->ime; - imc_release_ime( imc ); + if (ime->hkl == hkl) return ime; + imc->ime = NULL; + imc_release_ime( imc, ime ); } if (!(imc->ime = ime_acquire( hkl ))) @@ -623,12 +624,13 @@ BOOL WINAPI ImmActivateLayout( HKL hkl ) static BOOL free_input_context_data( HIMC hIMC ) { struct imc *data = query_imc_data( hIMC ); + struct ime *ime; if (!data) return FALSE; TRACE( "Destroying %p\n", hIMC ); - if (data->ime) imc_release_ime( data ); + if ((ime = imc_select_ime( data ))) imc_release_ime( data, ime ); ImmDestroyIMCC( data->IMC.hCompStr ); ImmDestroyIMCC( data->IMC.hCandInfo ); From be44cc0a15e5ba7cf0ea7e5dae38f8cbcc8bf246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 11:32:52 +0200 Subject: [PATCH 186/758] imm32/tests: Init INPUTCONTEXT status in ImeSelect. (cherry picked from commit 7d03937abe899db810c983779f2566b94d787e8a) --- dlls/imm32/tests/imm32.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ae2b5b2a4f2..76ef3bab521 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2477,6 +2477,7 @@ static void test_ImmDisableIME(void) #define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) +static BOOL ImeSelect_init_status; static BOOL todo_ImeInquire; DEFINE_EXPECT( ImeInquire ); static BOOL todo_ImeDestroy; @@ -2899,8 +2900,21 @@ static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select ) .hkl = GetKeyboardLayout( 0 ), .himc = himc, .func = IME_SELECT, .select = select }; + INPUTCONTEXT *ctx; + ime_trace( "himc %p, select %d\n", himc, select ); ime_calls[ime_call_count++] = call; + + if (ImeSelect_init_status && select) + { + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + ctx->fOpen = ~0; + ctx->fdwConversion = ~0; + ctx->fdwSentence = ~0; + ImmUnlockIMC( himc ); + } + return TRUE; } @@ -4300,10 +4314,15 @@ static void test_ImmActivateLayout(void) .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW}, .todo = TRUE, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + .todo = TRUE, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, - .todo = TRUE, .broken = (default_hkl == (HKL)0x04120412), + .todo = TRUE, }, { .hkl = expect_ime, .himc = default_himc, @@ -4768,6 +4787,7 @@ START_TEST(imm32) /* test these first to sanitize conversion / open statuses */ test_ImmSetConversionStatus(); test_ImmSetOpenStatus(); + ImeSelect_init_status = TRUE; test_ImmActivateLayout(); test_ImmCreateInputContext(); From 49149d9f2b7c82339d162ba915095d6e2ad34694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 23 Mar 2023 17:25:43 +0100 Subject: [PATCH 187/758] imm32/tests: Add some ImeSetActiveContext tests. (cherry picked from commit 3ed1bb2464fa15382933bfabb1ec8df049d41621) --- dlls/imm32/tests/imm32.c | 128 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 76ef3bab521..870e58c89db 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2504,6 +2504,7 @@ enum ime_function IME_SELECT = 1, IME_NOTIFY, IME_PROCESS_KEY, + IME_SET_ACTIVE_CONTEXT, MSG_IME_UI, }; @@ -2528,6 +2529,10 @@ struct ime_call LPARAM key_data; } process_key; struct + { + int flag; + } set_active_context; + struct { UINT msg; WPARAM wparam; @@ -2568,6 +2573,9 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done; if ((ret = expected->process_key.key_data - received->process_key.key_data)) goto done; break; + case IME_SET_ACTIVE_CONTEXT: + if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done; + break; case MSG_IME_UI: if ((ret = expected->message.msg - received->message.msg)) goto done; if ((ret = (expected->message.wparam - received->message.wparam))) goto done; @@ -2595,6 +2603,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", received->hkl, received->himc, received->process_key.vkey, received->process_key.key_data ); return ret; + case IME_SET_ACTIVE_CONTEXT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", received->hkl, received->himc, + received->set_active_context.flag ); + return ret; case MSG_IME_UI: todo_wine_if( expected->todo ) ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, @@ -2619,6 +2632,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.key_data ); break; + case IME_SET_ACTIVE_CONTEXT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", expected->hkl, expected->himc, + expected->set_active_context.flag ); + break; case MSG_IME_UI: todo_wine_if( expected->todo ) ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, @@ -2920,9 +2938,14 @@ static BOOL WINAPI ime_ImeSelect( HIMC himc, BOOL select ) static BOOL WINAPI ime_ImeSetActiveContext( HIMC himc, BOOL flag ) { + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = himc, + .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = flag} + }; ime_trace( "himc %p, flag %#x\n", himc, flag ); - ok( 0, "unexpected call\n" ); - return FALSE; + ime_calls[ime_call_count++] = call; + return TRUE; } static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len, @@ -4753,6 +4776,106 @@ static void test_DefWindowProc(void) ime_call_count = 0; } +static void test_ImmSetActiveContext(void) +{ + const struct ime_call activate_0_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 1} + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 1, .lparam = ISC_SHOWUIALL} + }, + {0}, + }; + const struct ime_call deactivate_0_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0} + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL} + }, + {0}, + }; + struct ime_call deactivate_1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_SELECT, .select = 1, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0} + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL} + }, + {0}, + }; + struct ime_call activate_1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 1} + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 1, .lparam = ISC_SHOWUIALL} + }, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HIMC himc; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) return; + + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + SetLastError( 0xdeadbeef ); + ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) ); + ok_seq( activate_0_seq ); + ok_ret( 0, GetLastError() ); + ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) ); + ok_seq( activate_0_seq ); + ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, FALSE ) ); + ok_seq( deactivate_0_seq ); + + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) ); + deactivate_1_seq[0].himc = himc; + deactivate_1_seq[1].himc = himc; + ok_seq( deactivate_1_seq ); + ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); + activate_1_seq[0].himc = himc; + ok_seq( activate_1_seq ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -4793,6 +4916,7 @@ START_TEST(imm32) test_ImmCreateInputContext(); test_ImmProcessKey(); test_DefWindowProc(); + test_ImmSetActiveContext(); if (init()) { From e8363dbe6f13589566fb658482a3a5e876dc4dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 12:50:14 +0200 Subject: [PATCH 188/758] imm32/tests: Add some spurious IME select calls. Seen with the Korean locale from time to time, probably caused by some uninitialized input context data. (cherry picked from commit 4a52781ec97b0d5094d0e1e47012a891ee5decfc) --- dlls/imm32/tests/imm32.c | 42 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 870e58c89db..bad9e5185a5 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4545,6 +4545,26 @@ static void test_ImmCreateInputContext(void) }; struct ime_call select1_seq[] = { + { + .hkl = expect_ime, .himc = 0/*himc[0]*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, + .todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, + .todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + .todo = TRUE, .broken = TRUE /* sometimes */, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + .todo = TRUE, .broken = TRUE /* sometimes */, + }, { .hkl = expect_ime, .himc = 0/*himc[1]*/, .func = IME_SELECT, .select = 1, @@ -4659,7 +4679,8 @@ static void test_ImmCreateInputContext(void) ok_seq( empty_sequence ); ctx = ImmLockIMC( himc[1] ); ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); - select1_seq[0].himc = himc[1]; + select1_seq[0].himc = himc[0]; + select1_seq[4].himc = himc[1]; ok_seq( select1_seq ); ok_ret( 1, ImmUnlockIMC( himc[1] ) ); @@ -4804,6 +4825,21 @@ static void test_ImmSetActiveContext(void) }; struct ime_call deactivate_1_seq[] = { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, + .todo = TRUE, .flaky_himc = TRUE, .broken = TRUE /* sometimes */, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + .todo = TRUE, .broken = TRUE /* sometimes */, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + .todo = TRUE, .broken = TRUE /* sometimes */, + }, { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_SELECT, .select = 1, @@ -4859,8 +4895,8 @@ static void test_ImmSetActiveContext(void) himc = ImmCreateContext(); ok_ne( NULL, himc, HIMC, "%p" ); ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) ); - deactivate_1_seq[0].himc = himc; - deactivate_1_seq[1].himc = himc; + deactivate_1_seq[3].himc = himc; + deactivate_1_seq[4].himc = himc; ok_seq( deactivate_1_seq ); ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); activate_1_seq[0].himc = himc; From 231d89789e4b3eb1b6a8782312895a117932ab06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 24 Mar 2023 23:29:56 +0100 Subject: [PATCH 189/758] imm32/tests: Add some ImmRequestMessageW tests. (cherry picked from commit e6e63828d36f143ac754fd6e12fca8ac9081b674) --- dlls/imm32/tests/imm32.c | 174 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index bad9e5185a5..ace3e5fc1fb 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2506,6 +2506,7 @@ enum ime_function IME_PROCESS_KEY, IME_SET_ACTIVE_CONTEXT, MSG_IME_UI, + MSG_TEST_WIN, }; struct ime_call @@ -2577,6 +2578,7 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done; break; case MSG_IME_UI: + case MSG_TEST_WIN: if ((ret = expected->message.msg - received->message.msg)) goto done; if ((ret = (expected->message.wparam - received->message.wparam))) goto done; if ((ret = (expected->message.lparam - received->message.lparam))) goto done; @@ -2613,6 +2615,10 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, received->himc, received->message.msg, received->message.wparam, received->message.lparam ); return ret; + case MSG_TEST_WIN: + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, + received->himc, received->message.msg, received->message.wparam, received->message.lparam ); + return ret; } switch (expected->func) @@ -2642,6 +2648,10 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); break; + case MSG_TEST_WIN: + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, + expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); + break; } return 0; @@ -2717,6 +2727,22 @@ static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, return DefWindowProcW( hwnd, msg, wparam, lparam ); } +static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = ImmGetContext( hwnd ), + .func = MSG_TEST_WIN, .message = {.msg = msg, .wparam = wparam, .lparam = lparam} + }; + + ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + + ime_calls[ime_call_count++] = call; + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + static WNDCLASSEXW ime_ui_class = { .cbSize = sizeof(WNDCLASSEXW), @@ -2726,6 +2752,13 @@ static WNDCLASSEXW ime_ui_class = .lpszClassName = L"WineTestIME", }; +static WNDCLASSEXW test_class = +{ + .cbSize = sizeof(WNDCLASSEXW), + .lpfnWndProc = test_window_proc, + .lpszClassName = L"WineTest", +}; + static BOOL WINAPI ime_ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) { ime_trace( "hkl %p, hwnd %p, mode %lu, data %p\n", hkl, hwnd, mode, data ); @@ -4912,10 +4945,148 @@ static void test_ImmSetActiveContext(void) ime_call_count = 0; } +static void test_ImmRequestMessage(void) +{ + struct ime_call composition_window_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_COMPOSITIONWINDOW, .lparam = 0/*&comp_form*/} + }, + {0}, + }; + struct ime_call candidate_window_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_CANDIDATEWINDOW, .lparam = 0/*&cand_form*/} + }, + {0}, + }; + struct ime_call composition_font_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_COMPOSITIONFONT, .lparam = 0/*&log_font*/} + }, + {0}, + }; + struct ime_call reconvert_string_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_RECONVERTSTRING, .lparam = 0/*&reconv*/} + }, + {0}, + }; + struct ime_call confirm_reconvert_string_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_CONFIRMRECONVERTSTRING, .lparam = 0/*&reconv*/} + }, + {0}, + }; + struct ime_call query_char_position_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_QUERYCHARPOSITION, .lparam = 0/*&char_pos*/} + }, + {0}, + }; + struct ime_call document_feed_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_REQUEST, .wparam = IMR_DOCUMENTFEED, .lparam = 0/*&reconv*/} + }, + {0}, + }; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + COMPOSITIONFORM comp_form = {0}; + IMECHARPOSITION char_pos = {0}; + RECONVERTSTRING reconv = {0}; + CANDIDATEFORM cand_form = {0}; + LOGFONTW log_font = {0}; + INPUTCONTEXT *ctx; + HIMC himc; + + if (!(hkl = ime_install())) return; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 0, ImmRequestMessageW( default_himc, 0xdeadbeef, 0 ) ); + todo_wine ok_seq( empty_sequence ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_COMPOSITIONWINDOW, (LPARAM)&comp_form ) ); + composition_window_seq[0].message.lparam = (LPARAM)&comp_form; + ok_seq( composition_window_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) ); + candidate_window_seq[0].message.lparam = (LPARAM)&cand_form; + ok_seq( candidate_window_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_COMPOSITIONFONT, (LPARAM)&log_font ) ); + composition_font_seq[0].message.lparam = (LPARAM)&log_font; + ok_seq( composition_font_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + todo_wine ok_seq( empty_sequence ); + reconv.dwSize = sizeof(RECONVERTSTRING); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + reconvert_string_seq[0].message.lparam = (LPARAM)&reconv; + ok_seq( reconvert_string_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + confirm_reconvert_string_seq[0].message.lparam = (LPARAM)&reconv; + ok_seq( confirm_reconvert_string_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + query_char_position_seq[0].message.lparam = (LPARAM)&char_pos; + ok_seq( query_char_position_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + document_feed_seq[0].message.lparam = (LPARAM)&reconv; + ok_seq( document_feed_seq ); + + ok_ret( 0, ImmRequestMessageW( himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) ); + ok_seq( empty_sequence ); + ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + ok_ret( 0, ImmRequestMessageW( himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) ); + candidate_window_seq[0].message.lparam = (LPARAM)&cand_form; + ok_seq( candidate_window_seq ); + ok_ret( 0, ImmRequestMessageW( default_himc, IMR_CANDIDATEWINDOW, (LPARAM)&cand_form ) ); + ok_seq( candidate_window_seq ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); + test_class.hInstance = GetModuleHandleW( NULL ); + RegisterClassExW( &test_class ); + if (!is_ime_enabled()) { win_skip("IME support not implemented\n"); @@ -4953,6 +5124,7 @@ START_TEST(imm32) test_ImmProcessKey(); test_DefWindowProc(); test_ImmSetActiveContext(); + test_ImmRequestMessage(); if (init()) { @@ -4985,4 +5157,6 @@ START_TEST(imm32) test_ImmDisableIME(); } cleanup(); + + UnregisterClassW( test_class.lpszClassName, test_class.hInstance ); } From 89de8805047b005cb9cceef4b7b22e7c116820ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 12:12:26 +0200 Subject: [PATCH 190/758] imm32/tests: Add some ImmGetCandidateList(W|A) tests. (cherry picked from commit 18d7be24ce398759ae0ca9a45db67926b0e7be30) --- dlls/imm32/tests/imm32.c | 156 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ace3e5fc1fb..23f9ae1a554 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -86,6 +86,31 @@ extern BOOL WINAPI ImmFreeLayout(HKL); extern BOOL WINAPI ImmLoadIME(HKL); extern BOOL WINAPI ImmActivateLayout(HKL); +#define check_member_( file, line, val, exp, fmt, member ) \ + ok_(file, line)( (val).member == (exp).member, "got " #member " " fmt "\n", (val).member ) +#define check_member( val, exp, fmt, member ) \ + check_member_( __FILE__, __LINE__, val, exp, fmt, member ) + +#define check_candidate_list( a, b ) check_candidate_list_( __LINE__, a, b, TRUE ) +static void check_candidate_list_( int line, CANDIDATELIST *list, const CANDIDATELIST *expect, BOOL unicode ) +{ + UINT i; + + check_member_( __FILE__, line, *list, *expect, "%lu", dwSize ); + check_member_( __FILE__, line, *list, *expect, "%lu", dwStyle ); + check_member_( __FILE__, line, *list, *expect, "%lu", dwCount ); + check_member_( __FILE__, line, *list, *expect, "%lu", dwSelection ); + check_member_( __FILE__, line, *list, *expect, "%lu", dwPageStart ); + check_member_( __FILE__, line, *list, *expect, "%lu", dwPageSize ); + for (i = 0; i < list->dwCount && i < expect->dwCount; ++i) + { + void *list_str = (BYTE *)list + list->dwOffset[i], *expect_str = (BYTE *)expect + expect->dwOffset[i]; + check_member_( __FILE__, line, *list, *expect, "%lu", dwOffset[i] ); + if (unicode) ok_( __FILE__, line )( !wcscmp( list_str, expect_str ), "got %s\n", debugstr_w(list_str) ); + else ok_( __FILE__, line )( !strcmp( list_str, expect_str ), "got %s\n", debugstr_a(list_str) ); + } +} + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE @@ -5080,6 +5105,134 @@ static void test_ImmRequestMessage(void) ime_call_count = 0; } +static void test_ImmGetCandidateList( BOOL unicode ) +{ + char buffer[512], expect_bufW[512] = {0}, expect_bufA[512] = {0}; + CANDIDATELIST *cand_list = (CANDIDATELIST *)buffer, *expect_listW, *expect_listA; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + CANDIDATEINFO *cand_info; + INPUTCONTEXT *ctx; + HIMC himc; + + expect_listW = (CANDIDATELIST *)expect_bufW; + expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32 * sizeof(WCHAR); + expect_listW->dwStyle = 0xdeadbeef; + expect_listW->dwCount = 2; + expect_listW->dwSelection = 3; + expect_listW->dwPageStart = 4; + expect_listW->dwPageSize = 5; + expect_listW->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]) + 2 * sizeof(WCHAR); + expect_listW->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 16 * sizeof(WCHAR); + wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[0]), L"Candidate 1" ); + wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[1]), L"Candidate 2" ); + + expect_listA = (CANDIDATELIST *)expect_bufA; + expect_listA->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32; + expect_listA->dwStyle = 0xdeadbeef; + expect_listA->dwCount = 2; + expect_listA->dwSelection = 3; + expect_listA->dwPageStart = 4; + expect_listA->dwPageSize = 5; + expect_listA->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]) + 2; + expect_listA->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 16; + strcpy( (char *)(expect_bufA + expect_listA->dwOffset[0]), "Candidate 1" ); + strcpy( (char *)(expect_bufA + expect_listA->dwOffset[1]), "Candidate 2" ); + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 0, ImmGetCandidateListW( default_himc, 0, NULL, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, ImmGetCandidateListW( default_himc, 1, NULL, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, ImmGetCandidateListW( default_himc, 0, cand_list, sizeof(buffer) ) ); + ok_seq( empty_sequence ); + + ok_ret( 0, ImmGetCandidateListW( himc, 0, cand_list, sizeof(buffer) ) ); + ok_seq( empty_sequence ); + + todo_wine ok_seq( empty_sequence ); + ctx->hCandInfo = ImmReSizeIMCC( ctx->hCandInfo, sizeof(*cand_info) + sizeof(buffer) ); + ok( !!ctx->hCandInfo, "ImmReSizeIMCC failed, error %lu\n", GetLastError() ); + + cand_info = ImmLockIMCC( ctx->hCandInfo ); + ok( !!cand_info, "ImmLockIMCC failed, error %lu\n", GetLastError() ); + cand_info->dwCount = 1; + cand_info->dwOffset[0] = sizeof(*cand_info); + if (unicode) memcpy( cand_info + 1, expect_bufW, sizeof(expect_bufW) ); + else memcpy( cand_info + 1, expect_bufA, sizeof(expect_bufA) ); + ok_ret( 0, ImmUnlockIMCC( ctx->hCandInfo ) ); + + ok_ret( (unicode ? 96 : 80), ImmGetCandidateListW( himc, 0, NULL, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, ImmGetCandidateListW( himc, 1, NULL, 0 ) ); + ok_seq( empty_sequence ); + memset( buffer, 0xcd, sizeof(buffer) ); + ok_ret( (unicode ? 96 : 80), ImmGetCandidateListW( himc, 0, cand_list, sizeof(buffer) ) ); + ok_seq( empty_sequence ); + + if (!unicode) + { + expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 24 * sizeof(WCHAR); + expect_listW->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]); + expect_listW->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 12 * sizeof(WCHAR); + wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[0]), L"Candidate 1" ); + wcscpy( (WCHAR *)(expect_bufW + expect_listW->dwOffset[1]), L"Candidate 2" ); + } + check_candidate_list_( __LINE__, cand_list, expect_listW, TRUE ); + + ok_ret( (unicode ? 56 : 64), ImmGetCandidateListA( himc, 0, NULL, 0 ) ); + ok_seq( empty_sequence ); + ok_ret( 0, ImmGetCandidateListA( himc, 1, NULL, 0 ) ); + ok_seq( empty_sequence ); + memset( buffer, 0xcd, sizeof(buffer) ); + ok_ret( (unicode ? 56 : 64), ImmGetCandidateListA( himc, 0, cand_list, sizeof(buffer) ) ); + ok_seq( empty_sequence ); + + if (unicode) + { + expect_listA->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 24; + expect_listA->dwOffset[0] = offsetof(CANDIDATELIST, dwOffset[2]); + expect_listA->dwOffset[1] = offsetof(CANDIDATELIST, dwOffset[2]) + 12; + strcpy( (char *)(expect_bufA + expect_listA->dwOffset[0]), "Candidate 1" ); + strcpy( (char *)(expect_bufA + expect_listA->dwOffset[1]), "Candidate 2" ); + } + check_candidate_list_( __LINE__, cand_list, expect_listA, FALSE ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + +cleanup: + winetest_pop_context(); +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -5126,6 +5279,9 @@ START_TEST(imm32) test_ImmSetActiveContext(); test_ImmRequestMessage(); + test_ImmGetCandidateList( TRUE ); + test_ImmGetCandidateList( FALSE ); + if (init()) { test_ImmNotifyIME(); From a6fbca937e57d9a77e4e9547b702415047112332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 07:59:03 +0200 Subject: [PATCH 191/758] imm32/tests: Add some ImmGetCandidateListCount(W|A) tests. (cherry picked from commit 91bb0bf54511c7376aa0e50f77a8bb21d1cf592c) --- dlls/imm32/tests/imm32.c | 75 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 23f9ae1a554..aa7e925f529 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -5233,6 +5233,79 @@ static void test_ImmGetCandidateList( BOOL unicode ) winetest_pop_context(); } +static void test_ImmGetCandidateListCount( BOOL unicode ) +{ + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + CANDIDATEINFO *cand_info; + INPUTCONTEXT *ctx; + DWORD count; + HIMC himc; + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 144, ImmGetCandidateListCountW( default_himc, &count ) ); + ok_eq( 0, count, UINT, "%u" ); + ok_seq( empty_sequence ); + ok_ret( 144, ImmGetCandidateListCountA( default_himc, &count ) ); + ok_eq( 0, count, UINT, "%u" ); + ok_seq( empty_sequence ); + + ok_ret( 144, ImmGetCandidateListCountW( himc, &count ) ); + ok_eq( 0, count, UINT, "%u" ); + ok_seq( empty_sequence ); + ok_ret( 144, ImmGetCandidateListCountA( himc, &count ) ); + ok_eq( 0, count, UINT, "%u" ); + ok_seq( empty_sequence ); + + cand_info = ImmLockIMCC( ctx->hCandInfo ); + ok( !!cand_info, "ImmLockIMCC failed, error %lu\n", GetLastError() ); + cand_info->dwCount = 1; + ok_ret( 0, ImmUnlockIMCC( ctx->hCandInfo ) ); + + todo_wine_if(!unicode) + ok_ret( (unicode ? 144 : 172), ImmGetCandidateListCountW( himc, &count ) ); + ok_eq( 1, count, UINT, "%u" ); + ok_seq( empty_sequence ); + todo_wine_if(unicode) + ok_ret( (unicode ? 172 : 144), ImmGetCandidateListCountA( himc, &count ) ); + ok_eq( 1, count, UINT, "%u" ); + ok_seq( empty_sequence ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + +cleanup: + winetest_pop_context(); +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -5281,6 +5354,8 @@ START_TEST(imm32) test_ImmGetCandidateList( TRUE ); test_ImmGetCandidateList( FALSE ); + test_ImmGetCandidateListCount( TRUE ); + test_ImmGetCandidateListCount( FALSE ); if (init()) { From 4cf3efb65f56eb84b768c6456ae19f5025edce84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 22:51:14 +0200 Subject: [PATCH 192/758] imm32/tests: Add some ImmGetCandidateWindow tests. (cherry picked from commit 19186b506321d66b0af6449b0c05890fe0179a31) --- dlls/imm32/tests/imm32.c | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index aa7e925f529..c18c76a71a2 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -91,6 +91,18 @@ extern BOOL WINAPI ImmActivateLayout(HKL); #define check_member( val, exp, fmt, member ) \ check_member_( __FILE__, __LINE__, val, exp, fmt, member ) +#define check_member_point_( file, line, val, exp, member ) \ + ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(POINT) ), \ + "got " #member " %s\n", wine_dbgstr_point( &(val).member ) ) +#define check_member_point( val, exp, member ) \ + check_member_point_( __FILE__, __LINE__, val, exp, member ) + +#define check_member_rect_( file, line, val, exp, member ) \ + ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(RECT) ), \ + "got " #member " %s\n", wine_dbgstr_rect( &(val).member ) ) +#define check_member_rect( val, exp, member ) \ + check_member_rect_( __FILE__, __LINE__, val, exp, member ) + #define check_candidate_list( a, b ) check_candidate_list_( __LINE__, a, b, TRUE ) static void check_candidate_list_( int line, CANDIDATELIST *list, const CANDIDATELIST *expect, BOOL unicode ) { @@ -111,6 +123,15 @@ static void check_candidate_list_( int line, CANDIDATELIST *list, const CANDIDAT } } +#define check_candidate_form( a, b ) check_candidate_form_( __LINE__, a, b ) +static void check_candidate_form_( int line, CANDIDATEFORM *form, const CANDIDATEFORM *expect ) +{ + check_member_( __FILE__, line, *form, *expect, "%#lx", dwIndex ); + check_member_( __FILE__, line, *form, *expect, "%#lx", dwStyle ); + check_member_point_( __FILE__, line, *form, *expect, ptCurrentPos ); + check_member_rect_( __FILE__, line, *form, *expect, rcArea ); +} + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE @@ -5306,6 +5327,80 @@ static void test_ImmGetCandidateListCount( BOOL unicode ) winetest_pop_context(); } +static void test_ImmGetCandidateWindow(void) +{ + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + const CANDIDATEFORM expect_form = + { + .dwIndex = 0xdeadbeef, + .dwStyle = 0xfeedcafe, + .ptCurrentPos = {.x = 123, .y = 456}, + .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4}, + }; + CANDIDATEFORM cand_form; + INPUTCONTEXT *ctx; + HIMC himc; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = ime_install())) return; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + memset( &cand_form, 0xcd, sizeof(cand_form) ); + ok_ret( 0, ImmGetCandidateWindow( default_himc, 0, &cand_form ) ); + ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" ); + ok_ret( 0, ImmGetCandidateWindow( default_himc, 1, &cand_form ) ); + ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" ); + ok_ret( 0, ImmGetCandidateWindow( default_himc, 2, &cand_form ) ); + ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" ); + ok_ret( 0, ImmGetCandidateWindow( default_himc, 3, &cand_form ) ); + ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" ); + todo_wine ok_ret( 1, ImmGetCandidateWindow( default_himc, 4, &cand_form ) ); + ok_seq( empty_sequence ); + + ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) ); + ok_seq( empty_sequence ); + + todo_wine ok_seq( empty_sequence ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ctx->cfCandForm[0] = expect_form; + + todo_wine ok_ret( 1, ImmGetCandidateWindow( himc, 0, &cand_form ) ); + todo_wine check_candidate_form( &cand_form, &expect_form ); + ok_seq( empty_sequence ); + + todo_wine ok_seq( empty_sequence ); + ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); + ctx->cfCandForm[0].dwIndex = -1; + + ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) ); + ok_seq( empty_sequence ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -5356,6 +5451,7 @@ START_TEST(imm32) test_ImmGetCandidateList( FALSE ); test_ImmGetCandidateListCount( TRUE ); test_ImmGetCandidateListCount( FALSE ); + test_ImmGetCandidateWindow(); if (init()) { From b5db4f4ca1ff8517d113f6bf69e61f2e3d9ac135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 11:24:08 +0200 Subject: [PATCH 193/758] winex11: Remove non-CJK specific XIC creation logic. (cherry picked from commit 41fa67c69ed040aabbfadb5add1e926ccb097bfc) --- dlls/winex11.drv/xim.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index d736dd80345..e04bf91206c 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -457,33 +457,10 @@ XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data) XIC xic; XICCallback destroy = {(XPointer)data, X11DRV_DestroyIC}; XICCallback P_StateNotifyCB, P_StartCB, P_DoneCB, P_DrawCB, P_CaretCB; - LCID lcid; Window win = data->whole_window; XFontSet fontSet = x11drv_thread_data()->font_set; - TRACE("xim = %p\n", xim); - - lcid = NtCurrentTeb()->CurrentLocale; - if (!lcid) NtQueryDefaultLocale( TRUE, &lcid ); - - /* use complex and slow XIC initialization method only for CJK */ - switch (PRIMARYLANGID(LANGIDFROMLCID(lcid))) - { - case LANG_CHINESE: - case LANG_JAPANESE: - case LANG_KOREAN: - break; - - default: - xic = XCreateIC(xim, - XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, win, - XNFocusWindow, win, - XNDestroyCallback, &destroy, - NULL); - data->xic = xic; - return xic; - } + TRACE( "xim %p, data %p\n", xim, data ); /* create callbacks */ P_StateNotifyCB.client_data = (XPointer)data; From 3e4a04c9daf7bfd4825c3264009005502b5a3220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 11:17:29 +0200 Subject: [PATCH 194/758] winex11: Always create XIC preedit and status attributes. (cherry picked from commit a1f5def2dc22e184deb0beb47640b4138ef2f581) --- dlls/winex11.drv/xim.c | 95 +++++++----------------------------------- 1 file changed, 14 insertions(+), 81 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index e04bf91206c..59a273d9ed3 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -452,8 +452,7 @@ static BOOL X11DRV_DestroyIC(XIC xic, XPointer p, XPointer data) XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data) { XPoint spot = {0}; - XVaNestedList preedit = NULL; - XVaNestedList status = NULL; + XVaNestedList preedit, status; XIC xic; XICCallback destroy = {(XPointer)data, X11DRV_DestroyIC}; XICCallback P_StateNotifyCB, P_StartCB, P_DoneCB, P_DrawCB, P_CaretCB; @@ -474,88 +473,22 @@ XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data) P_DrawCB.callback = (XICProc)XIMPreEditDrawCallback; P_CaretCB.callback = (XICProc)XIMPreEditCaretCallback; - if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0) - { - preedit = XVaCreateNestedList(0, - XNFontSet, fontSet, - XNSpotLocation, &spot, - XNPreeditStateNotifyCallback, &P_StateNotifyCB, - XNPreeditStartCallback, &P_StartCB, - XNPreeditDoneCallback, &P_DoneCB, - XNPreeditDrawCallback, &P_DrawCB, - XNPreeditCaretCallback, &P_CaretCB, - NULL); - TRACE("preedit = %p\n", preedit); - } - else - { - preedit = XVaCreateNestedList(0, - XNPreeditStateNotifyCallback, &P_StateNotifyCB, - XNPreeditStartCallback, &P_StartCB, - XNPreeditDoneCallback, &P_DoneCB, - XNPreeditDrawCallback, &P_DrawCB, - XNPreeditCaretCallback, &P_CaretCB, - NULL); - - TRACE("preedit = %p\n", preedit); - } - - if ((ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0) - { - status = XVaCreateNestedList(0, - XNFontSet, fontSet, - NULL); - TRACE("status = %p\n", status); - } - - if (preedit != NULL && status != NULL) - { - xic = XCreateIC(xim, - XNInputStyle, ximStyle, - XNPreeditAttributes, preedit, - XNStatusAttributes, status, - XNClientWindow, win, - XNFocusWindow, win, - XNDestroyCallback, &destroy, - NULL); - } - else if (preedit != NULL) - { - xic = XCreateIC(xim, - XNInputStyle, ximStyle, - XNPreeditAttributes, preedit, - XNClientWindow, win, - XNFocusWindow, win, - XNDestroyCallback, &destroy, - NULL); - } - else if (status != NULL) - { - xic = XCreateIC(xim, - XNInputStyle, ximStyle, - XNStatusAttributes, status, - XNClientWindow, win, - XNFocusWindow, win, - XNDestroyCallback, &destroy, - NULL); - } - else - { - xic = XCreateIC(xim, - XNInputStyle, ximStyle, - XNClientWindow, win, - XNFocusWindow, win, - XNDestroyCallback, &destroy, - NULL); - } + preedit = XVaCreateNestedList( 0, XNFontSet, fontSet, + XNPreeditCaretCallback, &P_CaretCB, + XNPreeditDoneCallback, &P_DoneCB, + XNPreeditDrawCallback, &P_DrawCB, + XNPreeditStartCallback, &P_StartCB, + XNPreeditStateNotifyCallback, &P_StateNotifyCB, + XNSpotLocation, &spot, NULL ); + status = XVaCreateNestedList( 0, XNFontSet, fontSet, NULL ); + xic = XCreateIC( xim, XNInputStyle, ximStyle, XNPreeditAttributes, preedit, XNStatusAttributes, status, + XNClientWindow, win, XNFocusWindow, win, XNDestroyCallback, &destroy, NULL ); + TRACE( "created XIC %p\n", xic ); - TRACE("xic = %p\n", xic); data->xic = xic; - if (preedit != NULL) - XFree(preedit); - if (status != NULL) - XFree(status); + XFree( preedit ); + XFree( status ); return xic; } From 4870b93839b1983d1292c220ba2b704f6fe83e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 11:28:43 +0200 Subject: [PATCH 195/758] winex11: Pass hwnd parameter to all XIC callbacks. (cherry picked from commit b91774a1ee6c4b2caf47943502a11b7ed3619f44) --- dlls/winex11.drv/window.c | 2 +- dlls/winex11.drv/x11drv.h | 2 +- dlls/winex11.drv/xim.c | 97 ++++++++++++++++++++++----------------- 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 43fd93536cc..ca19419bf99 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2781,7 +2781,7 @@ XIC X11DRV_get_ic( HWND hwnd ) { x11drv_thread_data()->last_xic_hwnd = hwnd; ret = data->xic; - if (!ret && (xim = x11drv_thread_data()->xim)) ret = X11DRV_CreateIC( xim, data ); + if (!ret && (xim = x11drv_thread_data()->xim)) ret = X11DRV_CreateIC( xim, hwnd, data ); release_win_data( data ); } return ret; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index f2b12a5c00d..f59fd63a037 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -877,7 +877,7 @@ extern struct x11drv_display_device_handler desktop_handler DECLSPEC_HIDDEN; /* XIM support */ extern BOOL X11DRV_InitXIM( const WCHAR *input_style ) DECLSPEC_HIDDEN; -extern XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data) DECLSPEC_HIDDEN; +extern XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) DECLSPEC_HIDDEN; extern void X11DRV_SetupXIM(void) DECLSPEC_HIDDEN; extern void X11DRV_XIMLookupChars( const char *str, UINT count ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 59a273d9ed3..7a647e4a643 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -109,12 +109,14 @@ void X11DRV_XIMLookupChars( const char *str, UINT count ) free( output ); } -static BOOL XIMPreEditStateNotifyCallback(XIC xic, XPointer p, XPointer data) +static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg ) { - const struct x11drv_win_data * const win_data = (struct x11drv_win_data *)p; - const XIMPreeditState state = ((XIMPreeditStateNotifyCallbackStruct *)data)->state; + XIMPreeditStateNotifyCallbackStruct *params = (void *)arg; + const XIMPreeditState state = params->state; + HWND hwnd = (HWND)user; + + TRACE( "xic %p, hwnd %p, state %lu\n", xic, hwnd, state ); - TRACE("xic = %p, win = %lx, state = %lu\n", xic, win_data->whole_window, state); switch (state) { case XIMPreeditEnable: @@ -130,17 +132,23 @@ static BOOL XIMPreEditStateNotifyCallback(XIC xic, XPointer p, XPointer data) return TRUE; } -static int XIMPreEditStartCallback(XIC ic, XPointer client_data, XPointer call_data) +static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) { - TRACE("PreEditStartCallback %p\n",ic); + HWND hwnd = (HWND)user; + + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); + x11drv_client_call( client_ime_set_composition_status, TRUE ); ximInComposeMode = TRUE; return -1; } -static void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_data) +static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) { - TRACE("PreeditDoneCallback %p\n",ic); + HWND hwnd = (HWND)user; + + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); + ximInComposeMode = FALSE; if (dwCompStringSize) free( CompositionString ); @@ -148,12 +156,15 @@ static void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_d dwCompStringLength = 0; CompositionString = NULL; x11drv_client_call( client_ime_set_composition_status, FALSE ); + return 0; } -static void XIMPreEditDrawCallback(XIM ic, XPointer client_data, - XIMPreeditDrawCallbackStruct *P_DR) +static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) { - TRACE("PreEditDrawCallback %p\n",ic); + XIMPreeditDrawCallbackStruct *P_DR = (void *)arg; + HWND hwnd = (HWND)user; + + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); if (P_DR) { @@ -189,13 +200,17 @@ static void XIMPreEditDrawCallback(XIM ic, XPointer client_data, X11DRV_ImmSetInternalString (sel, len, NULL, 0); x11drv_client_call( client_ime_set_cursor_pos, P_DR->caret ); } + TRACE("Finished\n"); + return 0; } -static void XIMPreEditCaretCallback(XIC ic, XPointer client_data, - XIMPreeditCaretCallbackStruct *P_C) +static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) { - TRACE("PreeditCaretCallback %p\n",ic); + XIMPreeditCaretCallbackStruct *P_C = (void *)arg; + HWND hwnd = (HWND)user; + + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); if (P_C) { @@ -219,7 +234,7 @@ static void XIMPreEditCaretCallback(XIC ic, XPointer client_data, break; case XIMDontChange: P_C->position = pos; - return; + return 0; case XIMCaretUp: case XIMCaretDown: case XIMPreviousLine: @@ -232,6 +247,7 @@ static void XIMPreEditCaretCallback(XIC ic, XPointer client_data, P_C->position = pos; } TRACE("Finished\n"); + return 0; } NTSTATUS x11drv_xim_reset( void *hwnd ) @@ -440,45 +456,44 @@ void X11DRV_SetupXIM(void) XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, open_xim_callback, NULL ); } -static BOOL X11DRV_DestroyIC(XIC xic, XPointer p, XPointer data) +static BOOL xic_destroy( XIC xic, XPointer user, XPointer arg ) { - struct x11drv_win_data *win_data = (struct x11drv_win_data *)p; - TRACE("xic = %p, win = %lx\n", xic, win_data->whole_window); - win_data->xic = NULL; + struct x11drv_win_data *data; + HWND hwnd = (HWND)user; + + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); + + if ((data = get_win_data( hwnd ))) + { + if (data->xic == xic) data->xic = NULL; + release_win_data( data ); + } + return TRUE; } - -XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data) +XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) { + XICCallback destroy = {.callback = xic_destroy, .client_data = (XPointer)hwnd}; + XICCallback preedit_caret = {.callback = xic_preedit_caret, .client_data = (XPointer)hwnd}; + XICCallback preedit_done = {.callback = xic_preedit_done, .client_data = (XPointer)hwnd}; + XICCallback preedit_draw = {.callback = xic_preedit_draw, .client_data = (XPointer)hwnd}; + XICCallback preedit_start = {.callback = xic_preedit_start, .client_data = (XPointer)hwnd}; + XICCallback preedit_state_notify = {.callback = xic_preedit_state_notify, .client_data = (XPointer)hwnd}; XPoint spot = {0}; XVaNestedList preedit, status; XIC xic; - XICCallback destroy = {(XPointer)data, X11DRV_DestroyIC}; - XICCallback P_StateNotifyCB, P_StartCB, P_DoneCB, P_DrawCB, P_CaretCB; Window win = data->whole_window; XFontSet fontSet = x11drv_thread_data()->font_set; - TRACE( "xim %p, data %p\n", xim, data ); - - /* create callbacks */ - P_StateNotifyCB.client_data = (XPointer)data; - P_StartCB.client_data = NULL; - P_DoneCB.client_data = NULL; - P_DrawCB.client_data = NULL; - P_CaretCB.client_data = NULL; - P_StateNotifyCB.callback = XIMPreEditStateNotifyCallback; - P_StartCB.callback = XIMPreEditStartCallback; - P_DoneCB.callback = (XICProc)XIMPreEditDoneCallback; - P_DrawCB.callback = (XICProc)XIMPreEditDrawCallback; - P_CaretCB.callback = (XICProc)XIMPreEditCaretCallback; + TRACE( "xim %p, hwnd %p, data %p\n", xim, hwnd, data ); preedit = XVaCreateNestedList( 0, XNFontSet, fontSet, - XNPreeditCaretCallback, &P_CaretCB, - XNPreeditDoneCallback, &P_DoneCB, - XNPreeditDrawCallback, &P_DrawCB, - XNPreeditStartCallback, &P_StartCB, - XNPreeditStateNotifyCallback, &P_StateNotifyCB, + XNPreeditCaretCallback, &preedit_caret, + XNPreeditDoneCallback, &preedit_done, + XNPreeditDrawCallback, &preedit_draw, + XNPreeditStartCallback, &preedit_start, + XNPreeditStateNotifyCallback, &preedit_state_notify, XNSpotLocation, &spot, NULL ); status = XVaCreateNestedList( 0, XNFontSet, fontSet, NULL ); xic = XCreateIC( xim, XNInputStyle, ximStyle, XNPreeditAttributes, preedit, XNStatusAttributes, status, From 1e9de18cd521c61067fdfe49c3826df96eefc8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 11:30:50 +0200 Subject: [PATCH 196/758] winex11: Remove unnecessary else control flow. (cherry picked from commit cc8a6b138449e300593db1be1cf0f51c54cd9579) --- dlls/winex11.drv/xim.c | 76 ++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 7a647e4a643..dda412e5eaf 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -368,53 +368,51 @@ static BOOL open_xim( Display *display ) XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL); if (ximStyles == 0) { - WARN("Could not find supported input style.\n"); - XCloseIM(xim); + WARN( "Could not find supported input style.\n" ); + XCloseIM( xim ); return FALSE; } - else - { - TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles); - ximStyleRoot = 0; - ximStyleNone = 0; + TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles); - for (i = 0; i < ximStyles->count_styles; ++i) + ximStyleRoot = 0; + ximStyleNone = 0; + + for (i = 0; i < ximStyles->count_styles; ++i) + { + int style = ximStyles->supported_styles[i]; + TRACE("ximStyles[%d] = %s%s%s%s%s\n", i, + (style&XIMPreeditArea)?"XIMPreeditArea ":"", + (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"", + (style&XIMPreeditPosition)?"XIMPreeditPosition ":"", + (style&XIMPreeditNothing)?"XIMPreeditNothing ":"", + (style&XIMPreeditNone)?"XIMPreeditNone ":""); + if (!ximStyle && (ximStyles->supported_styles[i] == + ximStyleRequest)) { - int style = ximStyles->supported_styles[i]; - TRACE("ximStyles[%d] = %s%s%s%s%s\n", i, - (style&XIMPreeditArea)?"XIMPreeditArea ":"", - (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"", - (style&XIMPreeditPosition)?"XIMPreeditPosition ":"", - (style&XIMPreeditNothing)?"XIMPreeditNothing ":"", - (style&XIMPreeditNone)?"XIMPreeditNone ":""); - if (!ximStyle && (ximStyles->supported_styles[i] == - ximStyleRequest)) - { - ximStyle = ximStyleRequest; - TRACE("Setting Style: ximStyle = ximStyleRequest\n"); - } - else if (!ximStyleRoot &&(ximStyles->supported_styles[i] == - STYLE_ROOT)) - { - ximStyleRoot = STYLE_ROOT; - TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n"); - } - else if (!ximStyleNone && (ximStyles->supported_styles[i] == - STYLE_NONE)) - { - TRACE("Setting Style: ximStyleNone = STYLE_NONE\n"); - ximStyleNone = STYLE_NONE; - } + ximStyle = ximStyleRequest; + TRACE("Setting Style: ximStyle = ximStyleRequest\n"); + } + else if (!ximStyleRoot &&(ximStyles->supported_styles[i] == + STYLE_ROOT)) + { + ximStyleRoot = STYLE_ROOT; + TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n"); } - XFree(ximStyles); + else if (!ximStyleNone && (ximStyles->supported_styles[i] == + STYLE_NONE)) + { + TRACE("Setting Style: ximStyleNone = STYLE_NONE\n"); + ximStyleNone = STYLE_NONE; + } + } + XFree(ximStyles); - if (ximStyle == 0) - ximStyle = ximStyleRoot; + if (ximStyle == 0) + ximStyle = ximStyleRoot; - if (ximStyle == 0) - ximStyle = ximStyleNone; - } + if (ximStyle == 0) + ximStyle = ximStyleNone; thread_data->xim = xim; From 7692d46de187b0dcfcf24a78dc5849a36309eda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:44:01 +0200 Subject: [PATCH 197/758] winex11: Create the thread XFontSet on thread attach. (cherry picked from commit 4390b0117633716b6e5477a35c13f6eb0fd52eff) --- dlls/winex11.drv/x11drv.h | 4 ++-- dlls/winex11.drv/x11drv_main.c | 4 ++-- dlls/winex11.drv/xim.c | 38 ++++++++++------------------------ 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index f59fd63a037..83d30143a6a 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -876,9 +876,9 @@ extern BOOL X11DRV_DisplayDevices_SupportEventHandlers(void) DECLSPEC_HIDDEN; extern struct x11drv_display_device_handler desktop_handler DECLSPEC_HIDDEN; /* XIM support */ -extern BOOL X11DRV_InitXIM( const WCHAR *input_style ) DECLSPEC_HIDDEN; +extern BOOL xim_init( const WCHAR *input_style ) DECLSPEC_HIDDEN; extern XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) DECLSPEC_HIDDEN; -extern void X11DRV_SetupXIM(void) DECLSPEC_HIDDEN; +extern void xim_thread_attach( struct x11drv_thread_data *data ) DECLSPEC_HIDDEN; extern void X11DRV_XIMLookupChars( const char *str, UINT count ) DECLSPEC_HIDDEN; #define XEMBED_MAPPED (1 << 0) diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index a767216ee1d..3ac5d0d167c 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -828,7 +828,7 @@ static NTSTATUS x11drv_init( void *arg ) XkbUseExtension( gdi_display, NULL, NULL ); X11DRV_InitKeyboard( gdi_display ); X11DRV_InitMouse( gdi_display ); - if (use_xim) use_xim = X11DRV_InitXIM( input_style ); + if (use_xim) use_xim = xim_init( input_style ); { const char *e = getenv("WINE_DISABLE_FULLSCREEN_HACK"); @@ -943,7 +943,7 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) set_queue_display_fd( data->display ); NtUserGetThreadInfo()->driver_data = (UINT_PTR)data; - if (use_xim) X11DRV_SetupXIM(); + if (use_xim) xim_thread_attach( data ); X11DRV_XInput2_Init(); if (NtUserGetWindowThread( NtUserGetDesktopWindow(), NULL ) == GetCurrentThreadId()) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index dda412e5eaf..0ab97edf5ba 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -288,13 +288,10 @@ NTSTATUS x11drv_xim_preedit_state( void *arg ) return 0; } - /*********************************************************************** - * X11DRV_InitXIM - * - * Process-wide XIM initialization. + * xim_init */ -BOOL X11DRV_InitXIM( const WCHAR *input_style ) +BOOL xim_init( const WCHAR *input_style ) { static const WCHAR offthespotW[] = {'o','f','f','t','h','e','s','p','o','t',0}; static const WCHAR overthespotW[] = {'o','v','e','r','t','h','e','s','p','o','t',0}; @@ -416,26 +413,6 @@ static BOOL open_xim( Display *display ) thread_data->xim = xim; - if ((ximStyle & (XIMPreeditNothing | XIMPreeditNone)) == 0 || - (ximStyle & (XIMStatusNothing | XIMStatusNone)) == 0) - { - char **list; - int count; - thread_data->font_set = XCreateFontSet(display, "fixed", - &list, &count, NULL); - TRACE("ximFontSet = %p\n", thread_data->font_set); - TRACE("list = %p, count = %d\n", list, count); - if (list != NULL) - { - int i; - for (i = 0; i < count; ++i) - TRACE("list[%d] = %s\n", i, list[i]); - XFreeStringList(list); - } - } - else - thread_data->font_set = NULL; - x11drv_client_call( client_ime_update_association, 0 ); return TRUE; } @@ -446,9 +423,16 @@ static void open_xim_callback( Display *display, XPointer ptr, XPointer data ) XUnregisterIMInstantiateCallback( display, NULL, NULL, NULL, open_xim_callback, NULL); } -void X11DRV_SetupXIM(void) +void xim_thread_attach( struct x11drv_thread_data *data ) { - Display *display = thread_display(); + Display *display = data->display; + int i, count; + char **list; + + data->font_set = XCreateFontSet( display, "fixed", &list, &count, NULL ); + TRACE( "created XFontSet %p, list %p, count %d\n", data->font_set, list, count ); + for (i = 0; list && i < count; ++i) TRACE( " %d: %s\n", i, list[i] ); + if (list) XFreeStringList( list ); if (!open_xim( display )) XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, open_xim_callback, NULL ); From 23ce4aeb3f32f23da147e6b459ba46799717975c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 14:06:19 +0200 Subject: [PATCH 198/758] winex11: Set thread data XIM pointer outside of open_xim. (cherry picked from commit f1f0386c1ec526496cb5e701a6329a97c318c599) --- dlls/winex11.drv/xim.c | 65 ++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 0ab97edf5ba..b213aa5b1fc 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -317,46 +317,26 @@ BOOL xim_init( const WCHAR *input_style ) return TRUE; } +static void xim_open( Display *display, XPointer user, XPointer arg ); +static void xim_destroy( XIM xim, XPointer user, XPointer arg ); -static void open_xim_callback( Display *display, XPointer ptr, XPointer data ); - -static void X11DRV_DestroyIM(XIM xim, XPointer p, XPointer data) -{ - struct x11drv_thread_data *thread_data = x11drv_thread_data(); - - TRACE("xim = %p, p = %p\n", xim, p); - thread_data->xim = NULL; - ximStyle = 0; - XRegisterIMInstantiateCallback( thread_data->display, NULL, NULL, NULL, open_xim_callback, NULL ); -} - -/*********************************************************************** - * X11DRV Ime creation - * - * Should always be called with the x11 lock held - */ -static BOOL open_xim( Display *display ) +static XIM xim_create( struct x11drv_thread_data *data ) { - struct x11drv_thread_data *thread_data = x11drv_thread_data(); + XIMCallback destroy = {.callback = xim_destroy, .client_data = (XPointer)data}; + Display *display = data->display; XIMStyle ximStyleNone; XIMStyles *ximStyles = NULL; INT i; XIM xim; - XIMCallback destroy; - xim = XOpenIM(display, NULL, NULL, NULL); - if (xim == NULL) + if (!(xim = XOpenIM( display, NULL, NULL, NULL ))) { WARN("Could not open input method.\n"); - return FALSE; + return NULL; } - destroy.client_data = NULL; - destroy.callback = X11DRV_DestroyIM; - if (XSetIMValues(xim, XNDestroyCallback, &destroy, NULL)) - { - WARN("Could not set destroy callback.\n"); - } + if (XSetIMValues( xim, XNDestroyCallback, &destroy, NULL )) + WARN( "Could not set destroy callback.\n" ); TRACE("xim = %p\n", xim); TRACE("X display of IM = %p\n", XDisplayOfIM(xim)); @@ -367,13 +347,14 @@ static BOOL open_xim( Display *display ) { WARN( "Could not find supported input style.\n" ); XCloseIM( xim ); - return FALSE; + return NULL; } TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles); ximStyleRoot = 0; ximStyleNone = 0; + ximStyle = 0; for (i = 0; i < ximStyles->count_styles; ++i) { @@ -411,16 +392,26 @@ static BOOL open_xim( Display *display ) if (ximStyle == 0) ximStyle = ximStyleNone; - thread_data->xim = xim; + return xim; +} + +static void xim_open( Display *display, XPointer user, XPointer arg ) +{ + struct x11drv_thread_data *data = (void *)user; + TRACE( "display %p, data %p, arg %p\n", display, user, arg ); + if (!(data->xim = xim_create( data ))) return; + XUnregisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, user ); x11drv_client_call( client_ime_update_association, 0 ); - return TRUE; } -static void open_xim_callback( Display *display, XPointer ptr, XPointer data ) +static void xim_destroy( XIM xim, XPointer user, XPointer arg ) { - if (open_xim( display )) - XUnregisterIMInstantiateCallback( display, NULL, NULL, NULL, open_xim_callback, NULL); + struct x11drv_thread_data *data = x11drv_thread_data(); + TRACE( "xim %p, user %p, arg %p\n", xim, user, arg ); + if (data->xim != xim) return; + data->xim = NULL; + XRegisterIMInstantiateCallback( data->display, NULL, NULL, NULL, xim_open, user ); } void xim_thread_attach( struct x11drv_thread_data *data ) @@ -434,8 +425,8 @@ void xim_thread_attach( struct x11drv_thread_data *data ) for (i = 0; list && i < count; ++i) TRACE( " %d: %s\n", i, list[i] ); if (list) XFreeStringList( list ); - if (!open_xim( display )) - XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, open_xim_callback, NULL ); + if ((data->xim = xim_create( data ))) x11drv_client_call( client_ime_update_association, 0 ); + else XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, (XPointer)data ); } static BOOL xic_destroy( XIC xic, XPointer user, XPointer arg ) From fcfe8b4411de380cf446b1a6feee83b7fd04995f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 14:08:20 +0200 Subject: [PATCH 199/758] winex11: Cleanup XIM initialization traces. (cherry picked from commit 64cca153292833151d1adeb185252ca57e2d1625) --- dlls/winex11.drv/xim.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index b213aa5b1fc..9f238e0cb83 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -61,6 +61,26 @@ static XIMStyle ximStyle = 0; static XIMStyle ximStyleRoot = 0; static XIMStyle ximStyleRequest = STYLE_CALLBACK; +static const char *debugstr_xim_style( XIMStyle style ) +{ + char buffer[1024], *buf = buffer; + + buf += sprintf( buf, "preedit" ); + if (style & XIMPreeditArea) buf += sprintf( buf, " area" ); + if (style & XIMPreeditCallbacks) buf += sprintf( buf, " callbacks" ); + if (style & XIMPreeditPosition) buf += sprintf( buf, " position" ); + if (style & XIMPreeditNothing) buf += sprintf( buf, " nothing" ); + if (style & XIMPreeditNone) buf += sprintf( buf, " none" ); + + buf += sprintf( buf, ", status" ); + if (style & XIMStatusArea) buf += sprintf( buf, " area" ); + if (style & XIMStatusCallbacks) buf += sprintf( buf, " callbacks" ); + if (style & XIMStatusNothing) buf += sprintf( buf, " nothing" ); + if (style & XIMStatusNone) buf += sprintf( buf, " none" ); + + return wine_dbg_sprintf( "%s", buffer ); +} + static void X11DRV_ImmSetInternalString(UINT offset, UINT selLength, LPWSTR lpComp, UINT len) { /* Composition strings are edited in chunks */ @@ -338,9 +358,8 @@ static XIM xim_create( struct x11drv_thread_data *data ) if (XSetIMValues( xim, XNDestroyCallback, &destroy, NULL )) WARN( "Could not set destroy callback.\n" ); - TRACE("xim = %p\n", xim); - TRACE("X display of IM = %p\n", XDisplayOfIM(xim)); - TRACE("Using %s locale of Input Method\n", XLocaleOfIM(xim)); + TRACE( "xim %p, XDisplayOfIM %p, XLocaleOfIM %s\n", xim, XDisplayOfIM( xim ), + debugstr_a(XLocaleOfIM( xim )) ); XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL); if (ximStyles == 0) @@ -350,21 +369,16 @@ static XIM xim_create( struct x11drv_thread_data *data ) return NULL; } - TRACE("ximStyles->count_styles = %d\n", ximStyles->count_styles); - ximStyleRoot = 0; ximStyleNone = 0; ximStyle = 0; + TRACE( "input styles count %u\n", ximStyles->count_styles ); for (i = 0; i < ximStyles->count_styles; ++i) { - int style = ximStyles->supported_styles[i]; - TRACE("ximStyles[%d] = %s%s%s%s%s\n", i, - (style&XIMPreeditArea)?"XIMPreeditArea ":"", - (style&XIMPreeditCallbacks)?"XIMPreeditCallbacks ":"", - (style&XIMPreeditPosition)?"XIMPreeditPosition ":"", - (style&XIMPreeditNothing)?"XIMPreeditNothing ":"", - (style&XIMPreeditNone)?"XIMPreeditNone ":""); + XIMStyle style = ximStyles->supported_styles[i]; + TRACE( " %u: %#lx %s\n", i, style, debugstr_xim_style( style ) ); + if (!ximStyle && (ximStyles->supported_styles[i] == ximStyleRequest)) { From ee9c055b270bf7f10af8d23b61795d63401123da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 16:45:44 +0200 Subject: [PATCH 200/758] imm32: Rewrite ImmInternalSendIMEMessage helper as imc_send_message. (cherry picked from commit f6cf1d4432b2843b789056f6e93a8c41596ef03b) --- dlls/imm32/imm.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e165544ba5e..e815f0c479b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -744,14 +744,11 @@ static void ImmInternalPostIMEMessage( struct imc *data, UINT msg, WPARAM wParam PostMessageW(target, msg, wParam, lParam); } -/* for sending messages as the IME */ -static void ImmInternalSendIMEMessage( struct imc *data, UINT msg, WPARAM wParam, LPARAM lParam ) +static void imc_send_message( struct imc *imc, TRANSMSG *message ) { - HWND target = GetFocus(); - if (!target) - SendMessageW(data->IMC.hWnd,msg,wParam,lParam); - else - SendMessageW(target, msg, wParam, lParam); + HWND target; + if (!(target = GetFocus()) && !(target = imc->IMC.hWnd)) return; + SendMessageW( target, message->message, message->wParam, message->lParam ); } static LRESULT ImmInternalSendIMENotify( struct imc *data, WPARAM notify, LPARAM lParam ) @@ -3014,9 +3011,7 @@ BOOL WINAPI ImmGenerateMessage(HIMC hIMC) data->IMC.dwNumMsgBuf = 0; lpTransMsg = ImmLockIMCC(hMsgBuf); - for (i = 0; i < dwNumMsgBuf; i++) - ImmInternalSendIMEMessage(data, lpTransMsg[i].message, lpTransMsg[i].wParam, lpTransMsg[i].lParam); - + for (i = 0; i < dwNumMsgBuf; i++) imc_send_message( data, lpTransMsg + i ); ImmUnlockIMCC(hMsgBuf); ImmDestroyIMCC(hMsgBuf); } From 77008343e3fe9195d657898f7cc6012e779b1b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 16:47:04 +0200 Subject: [PATCH 201/758] imm32: Rewrite ImmInternalPostIMEMessage helper as imc_post_message. (cherry picked from commit 3b4aa1662e79b025e46d4a10426eda504beb4c6b) --- dlls/imm32/imm.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e815f0c479b..de32196a133 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -734,14 +734,11 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) return TRUE; } -/* for posting messages as the IME */ -static void ImmInternalPostIMEMessage( struct imc *data, UINT msg, WPARAM wParam, LPARAM lParam ) +static void imc_post_message( struct imc *imc, TRANSMSG *message ) { - HWND target = GetFocus(); - if (!target) - PostMessageW(data->IMC.hWnd,msg,wParam,lParam); - else - PostMessageW(target, msg, wParam, lParam); + HWND target; + if (!(target = GetFocus()) && !(target = imc->IMC.hWnd)) return; + PostMessageW( target, message->message, message->wParam, message->lParam ); } static void imc_send_message( struct imc *imc, TRANSMSG *message ) @@ -3031,7 +3028,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD BYTE state[256]; UINT scancode; TRANSMSGLIST *list = NULL; - UINT msg_count; + UINT msg_count, i; UINT uVirtKey; static const DWORD list_count = 10; @@ -3063,13 +3060,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD msg_count = ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, imc ); TRACE("%i messages generated\n",msg_count); if (msg_count && msg_count <= list_count) - { - UINT i; - LPTRANSMSG msgs = list->TransMsg; - - for (i = 0; i < msg_count; i++) - ImmInternalPostIMEMessage(data, msgs[i].message, msgs[i].wParam, msgs[i].lParam); - } + for (i = 0; i < msg_count; i++) imc_post_message( data, list->TransMsg + i ); else if (msg_count > list_count) ImmGenerateMessage(imc); From c191fe88a77b959738b2f626f8a1fa8c58624f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 17:42:26 +0200 Subject: [PATCH 202/758] imm32: Rewrite ImmInternalSendIMENotify helper as imc_notify_ime. (cherry picked from commit 7f0c2c48df2dab74d73ccff518f43b3eb14dbc47) --- dlls/imm32/imm.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index de32196a133..31a0b927d07 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -748,17 +748,11 @@ static void imc_send_message( struct imc *imc, TRANSMSG *message ) SendMessageW( target, message->message, message->wParam, message->lParam ); } -static LRESULT ImmInternalSendIMENotify( struct imc *data, WPARAM notify, LPARAM lParam ) +static LRESULT imc_notify_ime( struct imc *imc, WPARAM notify, LPARAM lparam ) { HWND target; - - target = data->IMC.hWnd; - if (!target) target = GetFocus(); - - if (target) - return SendMessageW(target, WM_IME_NOTIFY, notify, lParam); - - return 0; + if (!(target = imc->IMC.hWnd) && !(target = GetFocus())) return 0; + return SendMessageW( target, WM_IME_NOTIFY, notify, lparam ); } /*********************************************************************** @@ -2368,7 +2362,7 @@ BOOL WINAPI ImmSetCandidateWindow( data->IMC.cfCandForm[lpCandidate->dwIndex] = *lpCandidate; ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS); - ImmInternalSendIMENotify(data, IMN_SETCANDIDATEPOS, 1 << lpCandidate->dwIndex); + imc_notify_ime( data, IMN_SETCANDIDATEPOS, 1 << lpCandidate->dwIndex ); return TRUE; } @@ -2393,7 +2387,7 @@ BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) MultiByteToWideChar(CP_ACP, 0, lplf->lfFaceName, -1, data->IMC.lfFont.W.lfFaceName, LF_FACESIZE); ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT); - ImmInternalSendIMENotify(data, IMN_SETCOMPOSITIONFONT, 0); + imc_notify_ime( data, IMN_SETCOMPOSITIONFONT, 0 ); return TRUE; } @@ -2416,7 +2410,7 @@ BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) data->IMC.lfFont.W = *lplf; ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT); - ImmInternalSendIMENotify(data, IMN_SETCOMPOSITIONFONT, 0); + imc_notify_ime( data, IMN_SETCOMPOSITIONFONT, 0 ); return TRUE; } @@ -2575,7 +2569,7 @@ BOOL WINAPI ImmSetCompositionWindow( if (ui_hwnd && reshow) ShowWindow( ui_hwnd, SW_SHOWNOACTIVATE ); - ImmInternalSendIMENotify(data, IMN_SETCOMPOSITIONWINDOW, 0); + imc_notify_ime( data, IMN_SETCOMPOSITIONWINDOW, 0 ); return TRUE; } @@ -2603,14 +2597,14 @@ BOOL WINAPI ImmSetConversionStatus( oldConversion = data->IMC.fdwConversion; data->IMC.fdwConversion = fdwConversion; ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, oldConversion, IMC_SETCONVERSIONMODE); - ImmInternalSendIMENotify(data, IMN_SETCONVERSIONMODE, 0); + imc_notify_ime( data, IMN_SETCONVERSIONMODE, 0 ); } if ( fdwSentence != data->IMC.fdwSentence ) { oldSentence = data->IMC.fdwSentence; data->IMC.fdwSentence = fdwSentence; ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, oldSentence, IMC_SETSENTENCEMODE); - ImmInternalSendIMENotify(data, IMN_SETSENTENCEMODE, 0); + imc_notify_ime( data, IMN_SETSENTENCEMODE, 0 ); } return TRUE; @@ -2640,7 +2634,7 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) { data->IMC.fOpen = fOpen; ImmNotifyIME( hIMC, NI_CONTEXTUPDATED, 0, IMC_SETOPENSTATUS); - ImmInternalSendIMENotify(data, IMN_SETOPENSTATUS, 0); + imc_notify_ime( data, IMN_SETOPENSTATUS, 0 ); } return TRUE; @@ -2667,7 +2661,7 @@ BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) data->IMC.ptStatusWndPos = *lpptPos; ImmNotifyIME( hIMC, NI_CONTEXTUPDATED, 0, IMC_SETSTATUSWINDOWPOS); - ImmInternalSendIMENotify(data, IMN_SETSTATUSWINDOWPOS, 0); + imc_notify_ime( data, IMN_SETSTATUSWINDOWPOS, 0 ); return TRUE; } From 7cef61eb3b868ac369842a0d50012ce10b1817a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 16:30:39 +0200 Subject: [PATCH 203/758] imm32: Fix mixed-up HIMC / imc pointers in ImmTranslateMessage. (cherry picked from commit 4445fd14d707347b0bcaf2fd5be0741586f519fa) --- dlls/imm32/imm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 31a0b927d07..a17ffa79e73 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3018,7 +3018,6 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD { struct imc *data; struct ime *ime; - HIMC imc = ImmGetContext(hwnd); BYTE state[256]; UINT scancode; TRANSMSGLIST *list = NULL; @@ -3028,8 +3027,8 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD TRACE("%p %x %x %x\n",hwnd, msg, (UINT)wParam, (UINT)lKeyData); - if (!(data = get_imc_data( imc ))) return FALSE; - if (!(ime = imc_select_ime( imc ))) return FALSE; + if (!(data = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; + if (!(ime = imc_select_ime( data ))) return FALSE; if (data->lastVK == VK_PROCESSKEY) return FALSE; GetKeyboardState(state); @@ -3051,12 +3050,12 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD else uVirtKey = data->lastVK; - msg_count = ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, imc ); + msg_count = ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, data->handle ); TRACE("%i messages generated\n",msg_count); if (msg_count && msg_count <= list_count) for (i = 0; i < msg_count; i++) imc_post_message( data, list->TransMsg + i ); else if (msg_count > list_count) - ImmGenerateMessage(imc); + ImmGenerateMessage( data->handle ); free( list ); From 26fc951fd20062ccb320218fa2c31cafc29502ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 18:32:29 +0200 Subject: [PATCH 204/758] imm32: Simplify control flow in ImmTranslateMessage. (cherry picked from commit 101743205d09919452e514c506a07f1d1fb2942f) --- dlls/imm32/imm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index a17ffa79e73..4609c689ed2 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3033,6 +3033,7 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD GetKeyboardState(state); scancode = lKeyData >> 0x10 & 0xff; + uVirtKey = data->lastVK; list = calloc( list_count, sizeof(TRANSMSG) + sizeof(DWORD) ); list->uMsgCount = list_count; @@ -3047,15 +3048,12 @@ BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyD ToUnicodeEx(data->lastVK, scancode, state, &chr, 1, 0, GetKeyboardLayout(0)); uVirtKey = MAKELONG(data->lastVK,chr); } - else - uVirtKey = data->lastVK; msg_count = ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, data->handle ); TRACE("%i messages generated\n",msg_count); - if (msg_count && msg_count <= list_count) - for (i = 0; i < msg_count; i++) imc_post_message( data, list->TransMsg + i ); - else if (msg_count > list_count) - ImmGenerateMessage( data->handle ); + + if (msg_count > list_count) ImmGenerateMessage( data->handle ); + else for (i = 0; i < msg_count; i++) imc_post_message( data, list->TransMsg + i ); free( list ); From 2cf71a9bf999a23566cebf7b75d1d186f1d058c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 18:23:38 +0200 Subject: [PATCH 205/758] imm32: Cleanup parameters and traces in ImmTranslateMessage. (cherry picked from commit 8d0e7d8cdcde897a9bf50a7047fc3bd4a503985c) --- dlls/imm32/imm.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 4609c689ed2..736e1dcf211 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3014,52 +3014,47 @@ BOOL WINAPI ImmGenerateMessage(HIMC hIMC) * ImmTranslateMessage(IMM32.@) * ( Undocumented, call internally and from user32.dll ) */ -BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData) +BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { + static const DWORD list_count = 10; + UINT scan, vkey, count, i; struct imc *data; struct ime *ime; BYTE state[256]; - UINT scancode; TRANSMSGLIST *list = NULL; - UINT msg_count, i; - UINT uVirtKey; - static const DWORD list_count = 10; + WCHAR chr; - TRACE("%p %x %x %x\n",hwnd, msg, (UINT)wParam, (UINT)lKeyData); + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); if (!(data = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; if (!(ime = imc_select_ime( data ))) return FALSE; if (data->lastVK == VK_PROCESSKEY) return FALSE; - GetKeyboardState(state); - scancode = lKeyData >> 0x10 & 0xff; - uVirtKey = data->lastVK; + GetKeyboardState( state ); + scan = lparam >> 0x10 & 0xff; + vkey = data->lastVK; list = calloc( list_count, sizeof(TRANSMSG) + sizeof(DWORD) ); list->uMsgCount = list_count; if (ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { - WCHAR chr; - - if (!ime_is_unicode( ime )) - ToAscii( data->lastVK, scancode, state, &chr, 0 ); - else - ToUnicodeEx(data->lastVK, scancode, state, &chr, 1, 0, GetKeyboardLayout(0)); - uVirtKey = MAKELONG(data->lastVK,chr); + if (!ime_is_unicode( ime )) ToAscii( data->lastVK, scan, state, &chr, 0 ); + else ToUnicodeEx( data->lastVK, scan, state, &chr, 1, 0, GetKeyboardLayout( 0 ) ); + vkey = MAKELONG( data->lastVK, chr ); } - msg_count = ime->pImeToAsciiEx( uVirtKey, scancode, state, list, 0, data->handle ); - TRACE("%i messages generated\n",msg_count); + count = ime->pImeToAsciiEx( vkey, scan, state, list, 0, data->handle ); + TRACE( "%u messages generated\n", count ); - if (msg_count > list_count) ImmGenerateMessage( data->handle ); - else for (i = 0; i < msg_count; i++) imc_post_message( data, list->TransMsg + i ); + if (count > list_count) ImmGenerateMessage( data->handle ); + else for (i = 0; i < count; i++) imc_post_message( data, list->TransMsg + i ); free( list ); data->lastVK = VK_PROCESSKEY; - return (msg_count > 0); + return count > 0; } /*********************************************************************** From 71b7f1a314eac2262c5490bd1589b905b0b90774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 18:33:10 +0200 Subject: [PATCH 206/758] imm32: Use a stack allocated buffer in ImmTranslateMessage. (cherry picked from commit 3385cda1c73d090bbb9ae030e49dcd51975b19ef) --- dlls/imm32/imm.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 736e1dcf211..e936bb27754 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3016,12 +3016,19 @@ BOOL WINAPI ImmGenerateMessage(HIMC hIMC) */ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { - static const DWORD list_count = 10; + union + { + struct + { + UINT uMsgCount; + TRANSMSG TransMsg[10]; + }; + TRANSMSGLIST list; + } buffer = {.uMsgCount = ARRAY_SIZE(buffer.TransMsg)}; UINT scan, vkey, count, i; struct imc *data; struct ime *ime; BYTE state[256]; - TRANSMSGLIST *list = NULL; WCHAR chr; TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); @@ -3034,9 +3041,6 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar scan = lparam >> 0x10 & 0xff; vkey = data->lastVK; - list = calloc( list_count, sizeof(TRANSMSG) + sizeof(DWORD) ); - list->uMsgCount = list_count; - if (ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { if (!ime_is_unicode( ime )) ToAscii( data->lastVK, scan, state, &chr, 0 ); @@ -3044,13 +3048,11 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar vkey = MAKELONG( data->lastVK, chr ); } - count = ime->pImeToAsciiEx( vkey, scan, state, list, 0, data->handle ); + count = ime->pImeToAsciiEx( vkey, scan, state, &buffer.list, 0, data->handle ); TRACE( "%u messages generated\n", count ); - if (count > list_count) ImmGenerateMessage( data->handle ); - else for (i = 0; i < count; i++) imc_post_message( data, list->TransMsg + i ); - - free( list ); + if (count > ARRAY_SIZE(buffer.TransMsg)) ImmGenerateMessage( data->handle ); + else for (i = 0; i < count; i++) imc_post_message( data, buffer.TransMsg + i ); data->lastVK = VK_PROCESSKEY; From b2d1542dad0e82ec09be9ddf6eb42bcde1026624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 15:33:10 +0200 Subject: [PATCH 207/758] winemac: Rename imeData / data members and variables to himc. (cherry picked from commit 158d6caabe4fe8776cfd9702d1c8de5d082fafc7) --- dlls/winemac.drv/cocoa_window.h | 2 +- dlls/winemac.drv/cocoa_window.m | 14 +++++++------- dlls/winemac.drv/event.c | 8 ++++---- dlls/winemac.drv/ime.c | 4 ++-- dlls/winemac.drv/macdrv_cocoa.h | 4 ++-- dlls/winemac.drv/unixlib.h | 4 ++-- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index a83f2aa803b..9539e4ebdd7 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -65,7 +65,7 @@ NSRect frameAtResizeStart; BOOL resizingFromLeft, resizingFromTop; - void* imeData; + void* himc; BOOL commandDone; NSSize savedContentMinSize; diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 2525c894d09..67e178c8ad3 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -403,7 +403,7 @@ @interface WineWindow () @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue; @property (nonatomic) BOOL usePerPixelAlpha; -@property (assign, nonatomic) void* imeData; +@property (assign, nonatomic) void* himc; @property (nonatomic) BOOL commandDone; @property (readonly, copy, nonatomic) NSArray* childWineWindows; @@ -714,7 +714,7 @@ - (void) completeText:(NSString*)text WineWindow* window = (WineWindow*)[self window]; event = macdrv_create_event(IM_SET_TEXT, window); - event->im_set_text.data = [window imeData]; + event->im_set_text.himc = [window himc]; event->im_set_text.text = (CFStringRef)[text copy]; event->im_set_text.complete = TRUE; @@ -797,7 +797,7 @@ - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replaceme markedTextSelection.location += replacementRange.location; event = macdrv_create_event(IM_SET_TEXT, window); - event->im_set_text.data = [window imeData]; + event->im_set_text.himc = [window himc]; event->im_set_text.text = (CFStringRef)[[markedText string] copy]; event->im_set_text.complete = FALSE; event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length; @@ -860,7 +860,7 @@ - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointe query = macdrv_create_query(); query->type = QUERY_IME_CHAR_RECT; query->window = (macdrv_window)[window retain]; - query->ime_char_rect.data = [window imeData]; + query->ime_char_rect.himc = [window himc]; query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length); if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait]) @@ -947,7 +947,7 @@ @implementation WineWindow @synthesize shapeChangedSinceLastDraw; @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue; @synthesize usePerPixelAlpha; - @synthesize imeData, commandDone; + @synthesize himc, commandDone; + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf windowFrame:(NSRect)window_frame @@ -3903,7 +3903,7 @@ uint32_t macdrv_window_background_color(void) /*********************************************************************** * macdrv_send_text_input_event */ -void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done) +void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* himc, int* done) { OnMainThreadAsync(^{ BOOL ret; @@ -3922,7 +3922,7 @@ void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, i CGEventRef c; NSEvent* event; - window.imeData = data; + window.himc = himc; fix_device_modifiers_by_generic(&localFlags); // An NSEvent created with +keyEventWithType:... is internally marked diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 5953dc0e0d8..03c49b34bae 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -154,7 +154,7 @@ static void macdrv_im_set_text(const macdrv_event *event) struct ime_set_text_params *params; CFIndex length = 0, size; - TRACE_(imm)("win %p/%p himc %p text %s complete %u\n", hwnd, event->window, event->im_set_text.data, + TRACE_(imm)("win %p/%p himc %p text %s complete %u\n", hwnd, event->window, event->im_set_text.himc, debugstr_cf(event->im_set_text.text), event->im_set_text.complete); if (event->im_set_text.text) @@ -163,7 +163,7 @@ static void macdrv_im_set_text(const macdrv_event *event) size = offsetof(struct ime_set_text_params, text[length]); if (!(params = malloc(size))) return; params->hwnd = HandleToUlong(hwnd); - params->data = (UINT_PTR)event->im_set_text.data; + params->himc = (UINT_PTR)event->im_set_text.himc; params->cursor_pos = event->im_set_text.cursor_pos; params->complete = event->im_set_text.complete; @@ -287,7 +287,7 @@ static BOOL query_drag_operation(macdrv_query *query) BOOL query_ime_char_rect(macdrv_query* query) { HWND hwnd = macdrv_get_window_hwnd(query->window); - void *himc = query->ime_char_rect.data; + void *himc = query->ime_char_rect.himc; CFRange *range = &query->ime_char_rect.range; CGRect *rect = &query->ime_char_rect.rect; struct ime_query_char_rect_result result = { .location = 0 }; @@ -298,7 +298,7 @@ BOOL query_ime_char_rect(macdrv_query* query) range->length); params.hwnd = HandleToUlong(hwnd); - params.data = (UINT_PTR)himc; + params.himc = (UINT_PTR)himc; params.result = (UINT_PTR)&result; params.location = range->location; params.length = range->length; diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index f779b4d06b0..5b422eec5c5 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -1322,7 +1322,7 @@ NTSTATUS WINAPI macdrv_ime_set_text(void *arg, ULONG size) { struct ime_set_text_params *params = arg; ULONG length = (size - offsetof(struct ime_set_text_params, text)) / sizeof(WCHAR); - void *himc = param_ptr(params->data); + void *himc = param_ptr(params->himc); HWND hwnd = UlongToHandle(params->hwnd); if (!himc) himc = RealIMC(FROM_MACDRV); @@ -1367,7 +1367,7 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) { struct ime_query_char_rect_params *params = arg; struct ime_query_char_rect_result *result = param_ptr(params->result); - void *himc = param_ptr(params->data); + void *himc = param_ptr(params->himc); IMECHARPOSITION charpos; BOOL ret = FALSE; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index a82dd319330..4f17d861785 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -378,7 +378,7 @@ typedef struct macdrv_event { unsigned long time_ms; } hotkey_press; struct { - void *data; + void *himc; CFStringRef text; /* new text or NULL if just completing existing text */ unsigned int cursor_pos; unsigned int complete; /* is completing text? */ @@ -487,7 +487,7 @@ typedef struct macdrv_query { CFTypeRef pasteboard; } drag_operation; struct { - void *data; + void *himc; CFRange range; CGRect rect; } ime_char_rect; diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 07f0da4a6f3..108ace8914c 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -177,7 +177,7 @@ struct ime_query_char_rect_params { UINT32 hwnd; UINT32 location; - UINT64 data; + UINT64 himc; UINT64 result; /* FIXME: Use NtCallbackReturn instead */ UINT32 length; }; @@ -187,7 +187,7 @@ struct ime_set_text_params { UINT32 hwnd; UINT32 cursor_pos; - UINT64 data; + UINT64 himc; UINT32 complete; WCHAR text[1]; }; From 92e6008fb62c0b6c3dd1fff3318377a2bccd697c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 21:00:09 +0200 Subject: [PATCH 208/758] winemac: Use UINT(32) for HIMC in the unixlib interface. (cherry picked from commit d69f799ced32d8a1e5a33a2f90bcb4cf8d4ee043) --- dlls/winemac.drv/ime.c | 2 +- dlls/winemac.drv/keyboard.c | 5 +++-- dlls/winemac.drv/macdrv_main.c | 4 ++-- dlls/winemac.drv/unixlib.h | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 5b422eec5c5..866998f1660 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -650,11 +650,11 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, UnlockRealIMC(hIMC); TRACE("Processing Mac 0x%04x\n", vkey); + params.himc = HandleToULong(hIMC); params.vkey = uVKey; params.scan = uScanCode; params.repeat = repeat; params.key_state = lpbKeyState; - params.himc = hIMC; params.done = &done; MACDRV_CALL(ime_process_text_input, ¶ms); diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 9e415bd70e3..bb6fb70fd2a 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -1195,12 +1195,13 @@ NTSTATUS macdrv_ime_process_text_input(void *arg) { struct process_text_input_params *params = arg; struct macdrv_thread_data *thread_data = macdrv_thread_data(); + void *himc = UlongToHandle(params->himc); const BYTE *key_state = params->key_state; unsigned int flags; int keyc; TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", params->vkey, params->scan, - params->repeat, params->himc); + params->repeat, himc); flags = thread_data->last_modifiers; if (key_state[VK_SHIFT] & 0x80) @@ -1233,7 +1234,7 @@ NTSTATUS macdrv_ime_process_text_input(void *arg) TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc); macdrv_send_text_input_event(((params->scan & 0x8000) == 0), flags, params->repeat, keyc, - params->himc, params->done); + himc, params->done); return 0; } diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index eeed9a4bcbe..a4b280283ee 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -667,20 +667,20 @@ static NTSTATUS wow64_ime_process_text_input(void *arg) { struct { + UINT himc; UINT vkey; UINT scan; UINT repeat; ULONG key_state; - ULONG himc; ULONG done; } *params32 = arg; struct process_text_input_params params; + params.himc = params32->himc; params.vkey = params32->vkey; params.scan = params32->scan; params.repeat = params32->repeat; params.key_state = UlongToPtr(params32->key_state); - params.himc = UlongToPtr(params32->himc); params.done = UlongToPtr(params32->done); return macdrv_ime_process_text_input(¶ms); } diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 108ace8914c..ca5115f4982 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -63,11 +63,11 @@ struct dnd_have_format_params /* macdrv_ime_process_text_input params */ struct process_text_input_params { + UINT himc; UINT vkey; UINT scan; UINT repeat; const BYTE *key_state; - void *himc; int *done; }; @@ -177,7 +177,7 @@ struct ime_query_char_rect_params { UINT32 hwnd; UINT32 location; - UINT64 himc; + UINT32 himc; UINT64 result; /* FIXME: Use NtCallbackReturn instead */ UINT32 length; }; @@ -187,7 +187,7 @@ struct ime_set_text_params { UINT32 hwnd; UINT32 cursor_pos; - UINT64 himc; + UINT32 himc; UINT32 complete; WCHAR text[1]; }; From 3478fc8252249bdf10b0e42872dfbea7a995a17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 11:23:32 +0200 Subject: [PATCH 209/758] winemac: Assume IME UI window always has a valid HIMC. (cherry picked from commit f16dcbbe470cb61edae2aa6f082de1343a01fb55) --- dlls/winemac.drv/ime.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 866998f1660..89341ba4f10 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -498,7 +498,7 @@ static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) LPCOMPOSITIONSTRING compstr; LPINPUTCONTEXT lpIMC; - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) return; @@ -517,7 +517,7 @@ static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) if (compstr != NULL) ImmUnlockIMCC(lpIMC->hCompStr); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } BOOL WINAPI ImeDestroy(UINT uForce) @@ -945,7 +945,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) INT offX = 0, offY = 0; LPINPUTCONTEXT lpIMC; - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) return; @@ -1060,7 +1060,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) ImmUnlockIMCC(lpIMC->hCompStr); EndPaint(hwnd, &ps); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) @@ -1074,14 +1074,14 @@ static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd) { LPINPUTCONTEXT lpIMC; - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) return; TRACE("IME message WM_IME_STARTCOMPOSITION\n"); lpIMC->hWnd = GetFocus(); ShowWindow(hwnd, SW_SHOWNOACTIVATE); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -1150,8 +1150,6 @@ static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM */ hIMC = (HIMC)GetWindowLongPtrW(hwnd, IMMGWL_IMC); - if (!hIMC) - hIMC = RealIMC(FROM_MACDRV); /* if we have no hIMC there are many messages we cannot process */ if (hIMC == NULL) @@ -1180,14 +1178,14 @@ static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM SetWindowTextA(hwnd, "Wine Ime Active"); - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC) { myPrivate = ImmLockIMCC(lpIMC->hPrivate); myPrivate->hwndDefault = hwnd; ImmUnlockIMCC(lpIMC->hPrivate); } - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); return TRUE; } From f29a55dc4107bda24fddd1d2c01471caf3663629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 18:58:50 +0200 Subject: [PATCH 210/758] winemac: Pass INPUTCONTEXT pointer to UpdateDataInDefaultIMEWindow. (cherry picked from commit 5f413c1ff58d086a8813bfce3385d26ce0f78134) --- dlls/winemac.drv/ime.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 89341ba4f10..d9fa3dd8712 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -493,14 +493,9 @@ static void IME_AddToSelected(HIMC hIMC) hSelectedFrom[hSelectedCount - 1] = hIMC; } -static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) +static void UpdateDataInDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd, BOOL showable) { LPCOMPOSITIONSTRING compstr; - LPINPUTCONTEXT lpIMC; - - lpIMC = ImmLockIMC(hIMC); - if (lpIMC == NULL) - return; if (lpIMC->hCompStr) compstr = ImmLockIMCC(lpIMC->hCompStr); @@ -516,8 +511,6 @@ static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) if (compstr != NULL) ImmUnlockIMCC(lpIMC->hCompStr); - - ImmUnlockIMC(hIMC); } BOOL WINAPI ImeDestroy(UINT uForce) @@ -674,8 +667,11 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, return msgs; } - else - UpdateDataInDefaultIMEWindow(hIMC, hwndDefault, FALSE); + else if ((lpIMC = LockRealIMC(hIMC))) + { + UpdateDataInDefaultIMEWindow(lpIMC, hwndDefault, FALSE); + UnlockRealIMC(hIMC); + } return 0; } @@ -1065,9 +1061,12 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) { + INPUTCONTEXT *ctx; TRACE("IME message WM_IME_COMPOSITION 0x%Ix\n", lParam); - if (!(lParam & GCS_RESULTSTR)) - UpdateDataInDefaultIMEWindow(hIMC, hwnd, TRUE); + if (lParam & GCS_RESULTSTR) return; + if (!(ctx = ImmLockIMC( hIMC ))) return; + UpdateDataInDefaultIMEWindow( ctx, hwnd, TRUE ); + ImmUnlockIMC(hIMC); } static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd) From dc6080a302898125f4b7bdf77cebbb5791317910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:01:36 +0200 Subject: [PATCH 211/758] winemac: Add a helper to get IME private window. (cherry picked from commit 7c09cae7f4eb23de107e1f5f0f9769f89a102fff) --- dlls/winemac.drv/ime.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index d9fa3dd8712..47e49dd0f32 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -44,7 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); #define FROM_MACDRV ((HIMC)0xcafe1337) -typedef struct _IMEPRIVATE { +typedef struct ime_private +{ BOOL bInComposition; BOOL bInternalState; HFONT textfont; @@ -67,6 +68,16 @@ static UINT WM_MSIME_RECONVERT; static UINT WM_MSIME_QUERYPOSITION; static UINT WM_MSIME_DOCUMENTFEED; +static HWND input_context_get_ui_hwnd( INPUTCONTEXT *ctx ) +{ + struct ime_private *priv; + HWND hwnd; + if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; + hwnd = priv->hwndDefault; + ImmUnlockIMCC( ctx->hPrivate ); + return hwnd; +} + static HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_MACDRV) @@ -543,13 +554,14 @@ BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lp if (lpIMC) { LPIMEPRIVATE myPrivate; + HWND hwnd = input_context_get_ui_hwnd( lpIMC ); myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (inIME && !myPrivate->bInternalState) ImmSetOpenStatus(RealIMC(FROM_MACDRV), TRUE); else if (!inIME && myPrivate->bInternalState) { - ShowWindow(myPrivate->hwndDefault, SW_HIDE); + ShowWindow( hwnd, SW_HIDE ); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = ImeCreateBlankCompStr(); ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); @@ -614,7 +626,7 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, UINT vkey; LPINPUTCONTEXT lpIMC; LPIMEPRIVATE myPrivate; - HWND hwndDefault; + HWND hwnd; UINT repeat; int done = 0; @@ -629,6 +641,7 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, } lpIMC = LockRealIMC(hIMC); + hwnd = input_context_get_ui_hwnd( lpIMC ); myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (!myPrivate->bInternalState) { @@ -638,7 +651,6 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, } repeat = myPrivate->repeat; - hwndDefault = myPrivate->hwndDefault; ImmUnlockIMCC(lpIMC->hPrivate); UnlockRealIMC(hIMC); @@ -669,7 +681,7 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, } else if ((lpIMC = LockRealIMC(hIMC))) { - UpdateDataInDefaultIMEWindow(lpIMC, hwndDefault, FALSE); + UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); UnlockRealIMC(hIMC); } return 0; @@ -1409,11 +1421,12 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) LPBYTE compdata = ImmLockIMCC(ic->hCompStr); LPCOMPOSITIONSTRING compstr = (LPCOMPOSITIONSTRING)compdata; LPWSTR str = (LPWSTR)(compdata + compstr->dwCompStrOffset); + HWND hwnd; - if (private->hwndDefault && compstr->dwCompStrOffset && - IsWindowVisible(private->hwndDefault)) + if ((hwnd = input_context_get_ui_hwnd( ic )) && IsWindowVisible( hwnd ) && + compstr->dwCompStrOffset) { - HDC dc = GetDC(private->hwndDefault); + HDC dc = GetDC( hwnd ); HFONT oldfont = NULL; SIZE size; @@ -1436,13 +1449,13 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) OffsetRect(&charpos.rcDocument, 10, 10); LPtoDP(dc, (POINT*)&charpos.rcDocument, 2); - MapWindowPoints(private->hwndDefault, 0, (POINT*)&charpos.rcDocument, 2); + MapWindowPoints( hwnd, 0, (POINT *)&charpos.rcDocument, 2 ); result->rect = charpos.rcDocument; ret = TRUE; if (oldfont) SelectObject(dc, oldfont); - ReleaseDC(private->hwndDefault, dc); + ReleaseDC( hwnd, dc ); } ImmUnlockIMCC(ic->hCompStr); From d6fe4fdc02565a040f4b05f83d05d9702e78e2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:56:41 +0200 Subject: [PATCH 212/758] winemac: Add a helper to get COMPOSITIONSTRING text. (cherry picked from commit fc37fdc44ea0f7e33b968f62a0b3b19ddade4c98) --- dlls/winemac.drv/ime.c | 77 ++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 47e49dd0f32..f99495d9a64 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -68,6 +68,27 @@ static UINT WM_MSIME_RECONVERT; static UINT WM_MSIME_QUERYPOSITION; static UINT WM_MSIME_DOCUMENTFEED; +static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) +{ + COMPOSITIONSTRING *string; + WCHAR *text = NULL; + UINT len, off; + + if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL; + len = result ? string->dwResultStrLen : string->dwCompStrLen; + off = result ? string->dwResultStrOffset : string->dwCompStrOffset; + + if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) ))) + { + memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) ); + text[len] = 0; + *length = len; + } + + ImmUnlockIMCC( ctx->hCompStr ); + return text; +} + static HWND input_context_get_ui_hwnd( INPUTCONTEXT *ctx ) { struct ime_private *priv; @@ -760,11 +781,9 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) case CPS_COMPLETE: { HIMCC newCompStr; - DWORD cplen = 0; - LPWSTR cpstr; - LPCOMPOSITIONSTRING cs = NULL; - LPBYTE cdata = NULL; LPIMEPRIVATE myPrivate; + WCHAR *str; + UINT len; TRACE("NI_COMPOSITIONSTR: CPS_COMPLETE\n"); @@ -774,21 +793,13 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; - if (lpIMC->hCompStr) - { - cdata = ImmLockIMCC(lpIMC->hCompStr); - cs = (LPCOMPOSITIONSTRING)cdata; - cplen = cs->dwCompStrLen; - cpstr = (LPWSTR)&cdata[cs->dwCompStrOffset]; - ImmUnlockIMCC(lpIMC->hCompStr); - } myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (cplen > 0) + if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { - WCHAR param = cpstr[0]; + WCHAR param = str[0]; DWORD flags = GCS_COMPSTR; - newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen); + newCompStr = updateResultStr( lpIMC->hCompStr, str, len ); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags); @@ -801,6 +812,7 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) GCS_RESULTSTR | GCS_RESULTCLAUSE); GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); + free( str ); } else if (myPrivate->bInComposition) GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); @@ -946,12 +958,12 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) PAINTSTRUCT ps; RECT rect; HDC hdc; - LPCOMPOSITIONSTRING compstr; - LPBYTE compdata = NULL; HMONITOR monitor; MONITORINFO mon_info; INT offX = 0, offY = 0; LPINPUTCONTEXT lpIMC; + WCHAR *str; + UINT len; lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) @@ -962,18 +974,13 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) GetClientRect(hwnd, &rect); FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1)); - compdata = ImmLockIMCC(lpIMC->hCompStr); - compstr = (LPCOMPOSITIONSTRING)compdata; - - if (compstr->dwCompStrLen && compstr->dwCompStrOffset) + if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { SIZE size; POINT pt; HFONT oldfont = NULL; - LPWSTR CompString; LPIMEPRIVATE myPrivate; - CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset); myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (myPrivate->textfont) @@ -981,7 +988,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) ImmUnlockIMCC(lpIMC->hPrivate); - GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size); + GetTextExtentPoint32W( hdc, str, len, &size ); pt.x = size.cx; pt.y = size.cy; LPtoDP(hdc, &pt, 1); @@ -1059,10 +1066,11 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE); - TextOutW(hdc, offX, offY, CompString, compstr->dwCompStrLen); + TextOutW( hdc, offX, offY, str, len ); if (oldfont) SelectObject(hdc, oldfont); + free( str ); } ImmUnlockIMCC(lpIMC->hCompStr); @@ -1418,13 +1426,12 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) if (ic) { LPIMEPRIVATE private = ImmLockIMCC(ic->hPrivate); - LPBYTE compdata = ImmLockIMCC(ic->hCompStr); - LPCOMPOSITIONSTRING compstr = (LPCOMPOSITIONSTRING)compdata; - LPWSTR str = (LPWSTR)(compdata + compstr->dwCompStrOffset); + WCHAR *str; HWND hwnd; + UINT len; if ((hwnd = input_context_get_ui_hwnd( ic )) && IsWindowVisible( hwnd ) && - compstr->dwCompStrOffset) + (str = input_context_get_comp_str( ic, FALSE, &len ))) { HDC dc = GetDC( hwnd ); HFONT oldfont = NULL; @@ -1433,15 +1440,13 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) if (private->textfont) oldfont = SelectObject(dc, private->textfont); - if (result->location > compstr->dwCompStrLen) - result->location = compstr->dwCompStrLen; - if (result->location + result->length > compstr->dwCompStrLen) - result->length = compstr->dwCompStrLen - result->location; + if (result->location > len) result->location = len; + if (result->location + result->length > len) result->length = len - result->location; - GetTextExtentPoint32W(dc, str, result->location, &size); + GetTextExtentPoint32W( dc, str, result->location, &size ); charpos.rcDocument.left = size.cx; charpos.rcDocument.top = 0; - GetTextExtentPoint32W(dc, str, result->location + result->length, &size); + GetTextExtentPoint32W( dc, str, result->location + result->length, &size ); charpos.rcDocument.right = size.cx; charpos.rcDocument.bottom = size.cy; @@ -1456,9 +1461,9 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) if (oldfont) SelectObject(dc, oldfont); ReleaseDC( hwnd, dc ); + free( str ); } - ImmUnlockIMCC(ic->hCompStr); ImmUnlockIMCC(ic->hPrivate); } From 62df52b511938babae7de5feca185e7c5b51ffd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:23:19 +0200 Subject: [PATCH 213/758] winemac: Add a helper to select IME private font. (cherry picked from commit addef4c979ac524bf44bae75c19190d8ae3218c5) --- dlls/winemac.drv/ime.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index f99495d9a64..5e78ae055fb 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -99,6 +99,16 @@ static HWND input_context_get_ui_hwnd( INPUTCONTEXT *ctx ) return hwnd; } +static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) +{ + struct ime_private *priv; + HFONT font = NULL; + if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; + if (priv->textfont) font = SelectObject( hdc, priv->textfont ); + ImmUnlockIMCC( ctx->hPrivate ); + return font; +} + static HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_MACDRV) @@ -976,17 +986,9 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { + HFONT font = input_context_select_ui_font( lpIMC, hdc ); SIZE size; POINT pt; - HFONT oldfont = NULL; - LPIMEPRIVATE myPrivate; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - - if (myPrivate->textfont) - oldfont = SelectObject(hdc, myPrivate->textfont); - - ImmUnlockIMCC(lpIMC->hPrivate); GetTextExtentPoint32W( hdc, str, len, &size ); pt.x = size.cx; @@ -1068,8 +1070,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) TextOutW( hdc, offX, offY, str, len ); - if (oldfont) - SelectObject(hdc, oldfont); + if (font) SelectObject( hdc, font ); free( str ); } @@ -1425,7 +1426,6 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) if (ic) { - LPIMEPRIVATE private = ImmLockIMCC(ic->hPrivate); WCHAR *str; HWND hwnd; UINT len; @@ -1434,12 +1434,9 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) (str = input_context_get_comp_str( ic, FALSE, &len ))) { HDC dc = GetDC( hwnd ); - HFONT oldfont = NULL; + HFONT font = input_context_select_ui_font( ic, dc ); SIZE size; - if (private->textfont) - oldfont = SelectObject(dc, private->textfont); - if (result->location > len) result->location = len; if (result->location + result->length > len) result->length = len - result->location; @@ -1458,13 +1455,10 @@ NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) result->rect = charpos.rcDocument; ret = TRUE; - if (oldfont) - SelectObject(dc, oldfont); + if (font) SelectObject( dc, font ); ReleaseDC( hwnd, dc ); free( str ); } - - ImmUnlockIMCC(ic->hPrivate); } ImmUnlockIMC(himc); From 81ff991aab147176b1743c0392d1e01b6da7b5e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 11:17:29 +0200 Subject: [PATCH 214/758] winex11: Assume IME UI window always has a valid HIMC. (cherry picked from commit fd9cd64b7950a461a746b452679efade875fd891) --- dlls/winex11.drv/ime.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 9c0894717ad..c494aaed2be 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -987,7 +987,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) INT offX=0, offY=0; LPINPUTCONTEXT lpIMC; - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) return; @@ -1101,7 +1101,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) ImmUnlockIMCC(lpIMC->hCompStr); EndPaint(hwnd,&ps); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } static void UpdateDefaultIMEWindow(HIMC hIMC, HWND hwnd) @@ -1109,7 +1109,7 @@ static void UpdateDefaultIMEWindow(HIMC hIMC, HWND hwnd) LPCOMPOSITIONSTRING compstr; LPINPUTCONTEXT lpIMC; - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) return; @@ -1130,7 +1130,7 @@ static void UpdateDefaultIMEWindow(HIMC hIMC, HWND hwnd) ImmUnlockIMCC(lpIMC->hCompStr); lpIMC->hWnd = GetFocus(); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) @@ -1214,8 +1214,6 @@ static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, */ hIMC = (HIMC)GetWindowLongPtrW(hwnd,IMMGWL_IMC); - if (!hIMC) - hIMC = RealIMC(FROM_X11); /* if we have no hIMC there are many messages we cannot process */ if (hIMC == NULL) @@ -1244,14 +1242,14 @@ static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, SetWindowTextA(hwnd,"Wine Ime Active"); - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC) { myPrivate = ImmLockIMCC(lpIMC->hPrivate); myPrivate->hwndDefault = hwnd; ImmUnlockIMCC(lpIMC->hPrivate); } - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); return TRUE; } From 6acaa0b7b9cc17ab8125f5f5975432b75b226567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 18:58:50 +0200 Subject: [PATCH 215/758] winex11: Pass INPUTCONTEXT pointer to UpdateDefaultIMEWindow. (cherry picked from commit b869270bef99e6f2f881c102a56e4576bdcf1304) --- dlls/winex11.drv/ime.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index c494aaed2be..8c7dac56251 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -1104,14 +1104,9 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) ImmUnlockIMC(hIMC); } -static void UpdateDefaultIMEWindow(HIMC hIMC, HWND hwnd) +static void UpdateDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd) { LPCOMPOSITIONSTRING compstr; - LPINPUTCONTEXT lpIMC; - - lpIMC = ImmLockIMC(hIMC); - if (lpIMC == NULL) - return; if (lpIMC->hCompStr) compstr = ImmLockIMCC(lpIMC->hCompStr); @@ -1130,20 +1125,25 @@ static void UpdateDefaultIMEWindow(HIMC hIMC, HWND hwnd) ImmUnlockIMCC(lpIMC->hCompStr); lpIMC->hWnd = GetFocus(); - ImmUnlockIMC(hIMC); } static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) { + INPUTCONTEXT *ctx; TRACE("IME message WM_IME_COMPOSITION 0x%Ix\n", lParam); - if (!(lParam & GCS_RESULTSTR)) - UpdateDefaultIMEWindow(hIMC, hwnd); + if (lParam & GCS_RESULTSTR) return; + if (!(ctx = ImmLockIMC( hIMC ))) return; + UpdateDefaultIMEWindow( ctx, hwnd ); + ImmUnlockIMC(hIMC); } static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd ) { + INPUTCONTEXT *ctx; TRACE("IME message WM_IME_STARTCOMPOSITION\n"); - UpdateDefaultIMEWindow(hIMC, hwnd); + if (!(ctx = ImmLockIMC( hIMC ))) return; + UpdateDefaultIMEWindow( ctx, hwnd ); + ImmUnlockIMC(hIMC); } static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, From e24f5b4de344e6628835b506833bcbfb48cbc84e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 20:42:39 +0200 Subject: [PATCH 216/758] winex11: Add a helper to get COMPOSITIONSTRING text. (cherry picked from commit 904ddc7eb5d5c31361d41d5aaaea36f1be341a0c) --- dlls/winex11.drv/ime.c | 60 +++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 8c7dac56251..050f273cf88 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -70,6 +70,27 @@ static UINT WM_MSIME_RECONVERT; static UINT WM_MSIME_QUERYPOSITION; static UINT WM_MSIME_DOCUMENTFEED; +static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) +{ + COMPOSITIONSTRING *string; + WCHAR *text = NULL; + UINT len, off; + + if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL; + len = result ? string->dwResultStrLen : string->dwCompStrLen; + off = result ? string->dwResultStrOffset : string->dwCompStrOffset; + + if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) ))) + { + memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) ); + text[len] = 0; + *length = len; + } + + ImmUnlockIMCC( ctx->hCompStr ); + return text; +} + static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); @@ -659,11 +680,9 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) case CPS_COMPLETE: { HIMCC newCompStr; - DWORD cplen = 0; - LPWSTR cpstr; - LPCOMPOSITIONSTRING cs = NULL; - LPBYTE cdata = NULL; LPIMEPRIVATE myPrivate; + WCHAR *str; + UINT len; TRACE("CPS_COMPLETE\n"); @@ -673,20 +692,12 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; - if (lpIMC->hCompStr) - { - cdata = ImmLockIMCC(lpIMC->hCompStr); - cs = (LPCOMPOSITIONSTRING)cdata; - cplen = cs->dwCompStrLen; - cpstr = (LPWSTR)&(cdata[cs->dwCompStrOffset]); - ImmUnlockIMCC(lpIMC->hCompStr); - } myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (cplen > 0) + if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { - WCHAR param = cpstr[0]; + WCHAR param = str[0]; - newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen); + newCompStr = updateResultStr( lpIMC->hCompStr, str, len ); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0); @@ -700,6 +711,7 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) GCS_RESULTSTR|GCS_RESULTCLAUSE); GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION, 0, 0); + free( str ); } else if (myPrivate->bInComposition) GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION, 0, 0); @@ -980,12 +992,12 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) PAINTSTRUCT ps; RECT rect; HDC hdc; - LPCOMPOSITIONSTRING compstr; - LPBYTE compdata = NULL; HMONITOR monitor; MONITORINFO mon_info; INT offX=0, offY=0; LPINPUTCONTEXT lpIMC; + WCHAR *str; + UINT len; lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) @@ -996,18 +1008,13 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) GetClientRect(hwnd,&rect); FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1)); - compdata = ImmLockIMCC(lpIMC->hCompStr); - compstr = (LPCOMPOSITIONSTRING)compdata; - - if (compstr->dwCompStrLen && compstr->dwCompStrOffset) + if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { SIZE size; POINT pt; HFONT oldfont = NULL; - LPWSTR CompString; LPIMEPRIVATE myPrivate; - CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset); myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (myPrivate->textfont) @@ -1015,7 +1022,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) ImmUnlockIMCC(lpIMC->hPrivate); - GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size); + GetTextExtentPoint32W( hdc, str, len, &size ); pt.x = size.cx; pt.y = size.cy; LPtoDP(hdc,&pt,1); @@ -1092,14 +1099,13 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE); - TextOutW(hdc, offX,offY, CompString, compstr->dwCompStrLen); + TextOutW( hdc, offX, offY, str, len ); if (oldfont) SelectObject(hdc,oldfont); + free( str ); } - ImmUnlockIMCC(lpIMC->hCompStr); - EndPaint(hwnd,&ps); ImmUnlockIMC(hIMC); } From f29f2f21ecfed634b27775b96c7e8af38311256b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:23:05 +0200 Subject: [PATCH 217/758] winex11: Add a helper to select IME private font. (cherry picked from commit bf9a649e10503f3346d836962c50fa5a28ccdf8a) --- dlls/winex11.drv/ime.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 050f273cf88..a499c8d9dd6 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -49,7 +49,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); #define FROM_X11 ((HIMC)0xcafe1337) -typedef struct _IMEPRIVATE { +typedef struct ime_private +{ BOOL bInComposition; BOOL bInternalState; HFONT textfont; @@ -91,6 +92,16 @@ static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT * return text; } +static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) +{ + struct ime_private *priv; + HFONT font = NULL; + if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; + if (priv->textfont) font = SelectObject( hdc, priv->textfont ); + ImmUnlockIMCC( ctx->hPrivate ); + return font; +} + static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); @@ -1010,17 +1021,9 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { + HFONT font = input_context_select_ui_font( lpIMC, hdc ); SIZE size; POINT pt; - HFONT oldfont = NULL; - LPIMEPRIVATE myPrivate; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - - if (myPrivate->textfont) - oldfont = SelectObject(hdc,myPrivate->textfont); - - ImmUnlockIMCC(lpIMC->hPrivate); GetTextExtentPoint32W( hdc, str, len, &size ); pt.x = size.cx; @@ -1101,8 +1104,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) TextOutW( hdc, offX, offY, str, len ); - if (oldfont) - SelectObject(hdc,oldfont); + if (font) SelectObject( hdc, font ); free( str ); } From 4f49cdb00ffb6ba8b94564e8a3c7df5a6cd736a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 13:42:00 +0200 Subject: [PATCH 218/758] winex11: Register XIC status callbacks. (cherry picked from commit 08bc39bd0eca69645fa3e816f47cfb8e682797dc) --- dlls/winex11.drv/xim.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 9f238e0cb83..1637549511b 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -270,6 +270,27 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) return 0; } +static int xic_status_start( XIC xic, XPointer user, XPointer arg ) +{ + HWND hwnd = (HWND)user; + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); + return 0; +} + +static int xic_status_done( XIC xic, XPointer user, XPointer arg ) +{ + HWND hwnd = (HWND)user; + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); + return 0; +} + +static int xic_status_draw( XIC xic, XPointer user, XPointer arg ) +{ + HWND hwnd = (HWND)user; + TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); + return 0; +} + NTSTATUS x11drv_xim_reset( void *hwnd ) { XIC ic = X11DRV_get_ic(hwnd); @@ -467,6 +488,9 @@ XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) XICCallback preedit_draw = {.callback = xic_preedit_draw, .client_data = (XPointer)hwnd}; XICCallback preedit_start = {.callback = xic_preedit_start, .client_data = (XPointer)hwnd}; XICCallback preedit_state_notify = {.callback = xic_preedit_state_notify, .client_data = (XPointer)hwnd}; + XICCallback status_done = {.callback = xic_status_done, .client_data = (XPointer)hwnd}; + XICCallback status_draw = {.callback = xic_status_draw, .client_data = (XPointer)hwnd}; + XICCallback status_start = {.callback = xic_status_start, .client_data = (XPointer)hwnd}; XPoint spot = {0}; XVaNestedList preedit, status; XIC xic; @@ -482,7 +506,11 @@ XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) XNPreeditStartCallback, &preedit_start, XNPreeditStateNotifyCallback, &preedit_state_notify, XNSpotLocation, &spot, NULL ); - status = XVaCreateNestedList( 0, XNFontSet, fontSet, NULL ); + status = XVaCreateNestedList( 0, XNFontSet, fontSet, + XNStatusStartCallback, &status_start, + XNStatusDoneCallback, &status_done, + XNStatusDrawCallback, &status_draw, + NULL ); xic = XCreateIC( xim, XNInputStyle, ximStyle, XNPreeditAttributes, preedit, XNStatusAttributes, status, XNClientWindow, win, XNFocusWindow, win, XNDestroyCallback, &destroy, NULL ); TRACE( "created XIC %p\n", xic ); From 723a55ec56492bb39c6be1dab4bb631498360d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 14:09:36 +0200 Subject: [PATCH 219/758] winex11: Refactor XIM input style selection. (cherry picked from commit d0691e8c9a0e4ebdbeacbbec92fa36f63d0cf1cc) --- dlls/winex11.drv/xim.c | 84 ++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 56 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 1637549511b..e73e019d448 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -49,17 +49,8 @@ static DWORD dwCompStringLength = 0; static LPBYTE CompositionString = NULL; static DWORD dwCompStringSize = 0; -#define STYLE_OFFTHESPOT (XIMPreeditArea | XIMStatusArea) -#define STYLE_OVERTHESPOT (XIMPreeditPosition | XIMStatusNothing) -#define STYLE_ROOT (XIMPreeditNothing | XIMStatusNothing) -/* this uses all the callbacks to utilize full IME support */ -#define STYLE_CALLBACK (XIMPreeditCallbacks | XIMStatusNothing) -/* in order to enable deadkey support */ -#define STYLE_NONE (XIMPreeditNothing | XIMStatusNothing) - -static XIMStyle ximStyle = 0; -static XIMStyle ximStyleRoot = 0; -static XIMStyle ximStyleRequest = STYLE_CALLBACK; +static XIMStyle input_style = 0; +static XIMStyle input_style_req = XIMPreeditCallbacks | XIMStatusCallbacks; static const char *debugstr_xim_style( XIMStyle style ) { @@ -338,13 +329,6 @@ BOOL xim_init( const WCHAR *input_style ) static const WCHAR overthespotW[] = {'o','v','e','r','t','h','e','s','p','o','t',0}; static const WCHAR rootW[] = {'r','o','o','t',0}; - if (!wcsicmp( input_style, offthespotW )) - ximStyleRequest = STYLE_OFFTHESPOT; - else if (!wcsicmp( input_style, overthespotW )) - ximStyleRequest = STYLE_OVERTHESPOT; - else if (!wcsicmp( input_style, rootW )) - ximStyleRequest = STYLE_ROOT; - if (!XSupportsLocale()) { WARN("X does not support locale.\n"); @@ -355,6 +339,17 @@ BOOL xim_init( const WCHAR *input_style ) WARN("Could not set locale modifiers.\n"); return FALSE; } + + if (!wcsicmp( input_style, offthespotW )) + input_style_req = XIMPreeditArea | XIMStatusArea; + else if (!wcsicmp( input_style, overthespotW )) + input_style_req = XIMPreeditPosition | XIMStatusNothing; + else if (!wcsicmp( input_style, rootW )) + input_style_req = XIMPreeditNothing | XIMStatusNothing; + + TRACE( "requesting %s style %#lx %s\n", debugstr_w(input_style), input_style_req, + debugstr_xim_style( input_style_req ) ); + return TRUE; } @@ -364,13 +359,12 @@ static void xim_destroy( XIM xim, XPointer user, XPointer arg ); static XIM xim_create( struct x11drv_thread_data *data ) { XIMCallback destroy = {.callback = xim_destroy, .client_data = (XPointer)data}; - Display *display = data->display; - XIMStyle ximStyleNone; - XIMStyles *ximStyles = NULL; + XIMStyle input_style_fallback = XIMPreeditNone | XIMStatusNone; + XIMStyles *styles = NULL; INT i; XIM xim; - if (!(xim = XOpenIM( display, NULL, NULL, NULL ))) + if (!(xim = XOpenIM( data->display, NULL, NULL, NULL ))) { WARN("Could not open input method.\n"); return NULL; @@ -382,50 +376,28 @@ static XIM xim_create( struct x11drv_thread_data *data ) TRACE( "xim %p, XDisplayOfIM %p, XLocaleOfIM %s\n", xim, XDisplayOfIM( xim ), debugstr_a(XLocaleOfIM( xim )) ); - XGetIMValues(xim, XNQueryInputStyle, &ximStyles, NULL); - if (ximStyles == 0) + XGetIMValues( xim, XNQueryInputStyle, &styles, NULL ); + if (!styles) { WARN( "Could not find supported input style.\n" ); XCloseIM( xim ); return NULL; } - ximStyleRoot = 0; - ximStyleNone = 0; - ximStyle = 0; - - TRACE( "input styles count %u\n", ximStyles->count_styles ); - for (i = 0; i < ximStyles->count_styles; ++i) + TRACE( "input styles count %u\n", styles->count_styles ); + for (i = 0, input_style = 0; i < styles->count_styles; ++i) { - XIMStyle style = ximStyles->supported_styles[i]; + XIMStyle style = styles->supported_styles[i]; TRACE( " %u: %#lx %s\n", i, style, debugstr_xim_style( style ) ); - if (!ximStyle && (ximStyles->supported_styles[i] == - ximStyleRequest)) - { - ximStyle = ximStyleRequest; - TRACE("Setting Style: ximStyle = ximStyleRequest\n"); - } - else if (!ximStyleRoot &&(ximStyles->supported_styles[i] == - STYLE_ROOT)) - { - ximStyleRoot = STYLE_ROOT; - TRACE("Setting Style: ximStyleRoot = STYLE_ROOT\n"); - } - else if (!ximStyleNone && (ximStyles->supported_styles[i] == - STYLE_NONE)) - { - TRACE("Setting Style: ximStyleNone = STYLE_NONE\n"); - ximStyleNone = STYLE_NONE; - } + if (style == input_style_req) input_style = style; + if (!input_style && (style & input_style_req)) input_style = style; + if (input_style_fallback > style) input_style_fallback = style; } - XFree(ximStyles); - - if (ximStyle == 0) - ximStyle = ximStyleRoot; + XFree(styles); - if (ximStyle == 0) - ximStyle = ximStyleNone; + if (!input_style) input_style = input_style_fallback; + TRACE( "selected style %#lx %s\n", input_style, debugstr_xim_style( input_style ) ); return xim; } @@ -511,7 +483,7 @@ XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) XNStatusDoneCallback, &status_done, XNStatusDrawCallback, &status_draw, NULL ); - xic = XCreateIC( xim, XNInputStyle, ximStyle, XNPreeditAttributes, preedit, XNStatusAttributes, status, + xic = XCreateIC( xim, XNInputStyle, input_style, XNPreeditAttributes, preedit, XNStatusAttributes, status, XNClientWindow, win, XNFocusWindow, win, XNDestroyCallback, &destroy, NULL ); TRACE( "created XIC %p\n", xic ); From 64bc57634e68ed49dbda742279ae04958f594f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 10:06:50 +0200 Subject: [PATCH 220/758] winex11: Reorder control flow in xic_preedit_draw. (cherry picked from commit 1a0ba03cd7530a1cd3568baa08aedf2f02cc419e) --- dlls/winex11.drv/xim.c | 55 ++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index e73e019d448..a60ee4b2042 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -172,47 +172,38 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) { - XIMPreeditDrawCallbackStruct *P_DR = (void *)arg; + XIMPreeditDrawCallbackStruct *params = (void *)arg; HWND hwnd = (HWND)user; + XIMText *text; TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); - if (P_DR) + if (!params) return 0; + + if (!(text = params->text)) + X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, NULL, 0 ); + else if (!text->encoding_is_wchar) { - int sel = P_DR->chg_first; - int len = P_DR->chg_length; - if (P_DR->text) + size_t text_len; + WCHAR *output; + + text_len = strlen( text->string.multi_byte ); + if ((output = malloc( text_len * sizeof(WCHAR) ))) { - if (! P_DR->text->encoding_is_wchar) - { - size_t text_len; - WCHAR *output; - - TRACE("multibyte\n"); - text_len = strlen( P_DR->text->string.multi_byte ); - if ((output = malloc( text_len * sizeof(WCHAR) ))) - { - text_len = ntdll_umbstowcs( P_DR->text->string.multi_byte, text_len, - output, text_len ); - - X11DRV_ImmSetInternalString( sel, len, output, text_len ); - free( output ); - } - } - else - { - FIXME("wchar PROBIBILY WRONG\n"); - X11DRV_ImmSetInternalString (sel, len, - (LPWSTR)P_DR->text->string.wide_char, - P_DR->text->length); - } + text_len = ntdll_umbstowcs( text->string.multi_byte, text_len, output, text_len ); + X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, output, text_len ); + free( output ); } - else - X11DRV_ImmSetInternalString (sel, len, NULL, 0); - x11drv_client_call( client_ime_set_cursor_pos, P_DR->caret ); + } + else + { + FIXME( "wchar PROBIBILY WRONG\n" ); + X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, + (WCHAR *)text->string.wide_char, text->length ); } - TRACE("Finished\n"); + x11drv_client_call( client_ime_set_cursor_pos, params->caret ); + return 0; } From 7f60f36f0ec91d3f9e8daeffd094cf0028257901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 10:18:07 +0200 Subject: [PATCH 221/758] winex11: Fix XIM wchar encoding in xic_preedit_draw. (cherry picked from commit 994d79903a096dccb905795a96492d87268ef6d3) --- dlls/winex11.drv/xim.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index a60ee4b2042..219e5b18974 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -182,24 +182,30 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (!(text = params->text)) X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, NULL, 0 ); - else if (!text->encoding_is_wchar) + else { size_t text_len; WCHAR *output; + char *str; + int len; + + if (!text->encoding_is_wchar) str = text->string.multi_byte; + else if ((len = wcstombs( NULL, text->string.wide_char, text->length )) < 0) str = NULL; + else if ((str = malloc( len + 1 ))) + { + wcstombs( str, text->string.wide_char, len ); + str[len] = 0; + } - text_len = strlen( text->string.multi_byte ); + text_len = str ? strlen( str ) : 0; if ((output = malloc( text_len * sizeof(WCHAR) ))) { - text_len = ntdll_umbstowcs( text->string.multi_byte, text_len, output, text_len ); + text_len = ntdll_umbstowcs( str, text_len, output, text_len ); X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, output, text_len ); free( output ); } - } - else - { - FIXME( "wchar PROBIBILY WRONG\n" ); - X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, - (WCHAR *)text->string.wide_char, text->length ); + + if (str != text->string.multi_byte) free( str ); } x11drv_client_call( client_ime_set_cursor_pos, params->caret ); From 4399fa0073f572661ed31677c50e4a498c5b7748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 10:21:22 +0200 Subject: [PATCH 222/758] winex11: Early return control flow in xic_preedit_caret. (cherry picked from commit d84f8dccf0b0e62bfba257db3bf2eed72f2bff0f) --- dlls/winex11.drv/xim.c | 67 +++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 219e5b18974..0251677eef9 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -215,46 +215,45 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) { - XIMPreeditCaretCallbackStruct *P_C = (void *)arg; + XIMPreeditCaretCallbackStruct *params = (void *)arg; HWND hwnd = (HWND)user; + int pos; TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); - if (P_C) + if (!params) return 0; + + pos = x11drv_client_call( client_ime_get_cursor_pos, 0 ); + switch (params->direction) { - int pos = x11drv_client_call( client_ime_get_cursor_pos, 0 ); - TRACE("pos: %d\n", pos); - switch(P_C->direction) - { - case XIMForwardChar: - case XIMForwardWord: - pos++; - break; - case XIMBackwardChar: - case XIMBackwardWord: - pos--; - break; - case XIMLineStart: - pos = 0; - break; - case XIMAbsolutePosition: - pos = P_C->position; - break; - case XIMDontChange: - P_C->position = pos; - return 0; - case XIMCaretUp: - case XIMCaretDown: - case XIMPreviousLine: - case XIMNextLine: - case XIMLineEnd: - FIXME("Not implemented\n"); - break; - } - x11drv_client_call( client_ime_set_cursor_pos, pos ); - P_C->position = pos; + case XIMForwardChar: + case XIMForwardWord: + pos++; + break; + case XIMBackwardChar: + case XIMBackwardWord: + pos--; + break; + case XIMLineStart: + pos = 0; + break; + case XIMAbsolutePosition: + pos = params->position; + break; + case XIMDontChange: + params->position = pos; + return 0; + case XIMCaretUp: + case XIMCaretDown: + case XIMPreviousLine: + case XIMNextLine: + case XIMLineEnd: + FIXME( "Not implemented\n" ); + break; } - TRACE("Finished\n"); + x11drv_client_call( client_ime_set_cursor_pos, pos ); + params->position = pos; + return 0; } From 694598ec93c8e1237cd20bcc1fdd59413fdd6c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 22:54:20 +0200 Subject: [PATCH 223/758] winex11: Set x11drv_win_data XIC out of X11DRV_CreateIC. (cherry picked from commit 2be535a221cd324f8da172c4bdb677f6d59dae59) --- dlls/winex11.drv/window.c | 22 ---------------------- dlls/winex11.drv/x11drv.h | 3 +-- dlls/winex11.drv/xim.c | 22 +++++++++++++++++----- 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ca19419bf99..417a51d56f1 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2766,28 +2766,6 @@ Window X11DRV_get_whole_window( HWND hwnd ) } -/*********************************************************************** - * X11DRV_get_ic - * - * Return the X input context associated with a window - */ -XIC X11DRV_get_ic( HWND hwnd ) -{ - struct x11drv_win_data *data = get_win_data( hwnd ); - XIM xim; - XIC ret = 0; - - if (data) - { - x11drv_thread_data()->last_xic_hwnd = hwnd; - ret = data->xic; - if (!ret && (xim = x11drv_thread_data()->xim)) ret = X11DRV_CreateIC( xim, hwnd, data ); - release_win_data( data ); - } - return ret; -} - - /*********************************************************************** * X11DRV_GetDC (X11DRV.@) */ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 83d30143a6a..8a035f3a5c1 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -666,7 +666,6 @@ struct x11drv_win_data extern struct x11drv_win_data *get_win_data( HWND hwnd ) DECLSPEC_HIDDEN; extern void release_win_data( struct x11drv_win_data *data ) DECLSPEC_HIDDEN; extern Window X11DRV_get_whole_window( HWND hwnd ) DECLSPEC_HIDDEN; -extern XIC X11DRV_get_ic( HWND hwnd ) DECLSPEC_HIDDEN; extern Window get_dummy_parent(void) DECLSPEC_HIDDEN; extern void sync_gl_drawable( HWND hwnd, BOOL known_child ) DECLSPEC_HIDDEN; @@ -877,9 +876,9 @@ extern struct x11drv_display_device_handler desktop_handler DECLSPEC_HIDDEN; /* XIM support */ extern BOOL xim_init( const WCHAR *input_style ) DECLSPEC_HIDDEN; -extern XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) DECLSPEC_HIDDEN; extern void xim_thread_attach( struct x11drv_thread_data *data ) DECLSPEC_HIDDEN; extern void X11DRV_XIMLookupChars( const char *str, UINT count ) DECLSPEC_HIDDEN; +extern XIC X11DRV_get_ic( HWND hwnd ) DECLSPEC_HIDDEN; #define XEMBED_MAPPED (1 << 0) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 0251677eef9..b7f5f696ba5 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -448,7 +448,7 @@ static BOOL xic_destroy( XIC xic, XPointer user, XPointer arg ) return TRUE; } -XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) +static XIC xic_create( XIM xim, HWND hwnd, Window win ) { XICCallback destroy = {.callback = xic_destroy, .client_data = (XPointer)hwnd}; XICCallback preedit_caret = {.callback = xic_preedit_caret, .client_data = (XPointer)hwnd}; @@ -462,10 +462,9 @@ XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) XPoint spot = {0}; XVaNestedList preedit, status; XIC xic; - Window win = data->whole_window; XFontSet fontSet = x11drv_thread_data()->font_set; - TRACE( "xim %p, hwnd %p, data %p\n", xim, hwnd, data ); + TRACE( "xim %p, hwnd %p/%lx\n", xim, hwnd, win ); preedit = XVaCreateNestedList( 0, XNFontSet, fontSet, XNPreeditCaretCallback, &preedit_caret, @@ -483,10 +482,23 @@ XIC X11DRV_CreateIC( XIM xim, HWND hwnd, struct x11drv_win_data *data ) XNClientWindow, win, XNFocusWindow, win, XNDestroyCallback, &destroy, NULL ); TRACE( "created XIC %p\n", xic ); - data->xic = xic; - XFree( preedit ); XFree( status ); return xic; } + +XIC X11DRV_get_ic( HWND hwnd ) +{ + struct x11drv_win_data *data; + XIM xim; + XIC ret; + + if (!(data = get_win_data( hwnd ))) return 0; + x11drv_thread_data()->last_xic_hwnd = hwnd; + if (!(ret = data->xic) && (xim = x11drv_thread_data()->xim)) + ret = data->xic = xic_create( xim, hwnd, data->whole_window ); + release_win_data( data ); + + return ret; +} From 07d8d9774e9e3e4895374ba11e58c5916d771203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 16:24:08 +0200 Subject: [PATCH 224/758] imm32/tests: Test setting the same HIMC statuses twice. (cherry picked from commit 8cb2d2d54137247148fd3dcdfba18b669ae1778e) --- dlls/imm32/tests/imm32.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index c18c76a71a2..77c868cf168 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4118,6 +4118,14 @@ static void test_ImmSetConversionStatus(void) ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" ); ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" ); + ok_ret( 1, ImmSetConversionStatus( default_himc, 0xdeadbeef, 0xfeedcafe ) ); + ok_seq( empty_sequence ); + + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, NULL ) ); + ok_eq( 0xdeadbeef, conversion, UINT, "%#x" ); + ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" ); + ok_seq( empty_sequence ); ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0xfeedcafe ) ); ok_seq( set_conversion_status_1_seq ); @@ -4132,6 +4140,14 @@ static void test_ImmSetConversionStatus(void) ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) ); ok_seq( set_conversion_status_2_seq ); + ok_ret( 1, ImmGetConversionStatus( default_himc, NULL, &sentence ) ); + ok_eq( ~0, sentence, UINT, "%#x" ); + ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); + + ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) ); + ok_seq( empty_sequence ); + ok_ret( 1, ImmGetConversionStatus( default_himc, &conversion, &sentence ) ); ok_eq( ~0, conversion, UINT, "%#x" ); ok_eq( ~0, sentence, UINT, "%#x" ); @@ -4245,6 +4261,13 @@ static void test_ImmSetOpenStatus(void) ok_eq( 0xdeadbeef, status, UINT, "%#x" ); ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) ); + ok_seq( empty_sequence ); + + status = ImmGetOpenStatus( default_himc ); + ok_eq( 0xdeadbeef, status, UINT, "%#x" ); + ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + ok_seq( empty_sequence ); ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) ); ok_seq( set_open_status_1_seq ); @@ -4253,6 +4276,13 @@ static void test_ImmSetOpenStatus(void) todo_wine ok_eq( ~0, status, UINT, "%#x" ); todo_wine ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); + ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) ); + ok_seq( empty_sequence ); + + status = ImmGetOpenStatus( default_himc ); + todo_wine ok_eq( ~0, status, UINT, "%#x" ); + todo_wine ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); + /* status is cached between IME activations */ ok_ret( 1, ImmActivateLayout( old_hkl ) ); From 22cd3e37c52004f5432e6f93a755037d68823a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 00:47:15 +0200 Subject: [PATCH 225/758] imm32/tests: Test WM_IME_NOTIFY messages target window. Showing that they aren't sent to the focused window but only to the INPUTCONTEXT hWnd member. (cherry picked from commit 0522c01be32b56b1e295c7d683a4d63b0d86fc79) --- dlls/imm32/tests/imm32.c | 61 ++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 77c868cf168..b064e44e578 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2662,6 +2662,7 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected received->himc, received->message.msg, received->message.wparam, received->message.lparam ); return ret; case MSG_TEST_WIN: + todo_wine_if( expected->todo ) ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, received->himc, received->message.msg, received->message.wparam, received->message.lparam ); return ret; @@ -2695,6 +2696,7 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); break; case MSG_TEST_WIN: + todo_wine_if( expected->todo ) ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); break; @@ -2714,7 +2716,8 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "", !received->func ? " (missing)" : "" ); ret = ok_call_( file, line, expected, received ); - if (ret && expected->todo && !strcmp( winetest_platform, "wine" )) + if (ret && expected->todo && expected->func && + !strcmp( winetest_platform, "wine" )) expected++; else if (ret && broken(expected->broken)) expected++; @@ -4014,6 +4017,10 @@ static void test_ImmSetConversionStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE}, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, @@ -4022,6 +4029,10 @@ static void test_ImmSetConversionStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSENTENCEMODE}, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, @@ -4034,10 +4045,7 @@ static void test_ImmSetConversionStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xdeadbeef, .value = IMC_SETCONVERSIONMODE}, }, - { - .hkl = expect_ime, .himc = default_himc, - .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, - }, + {.todo = TRUE}, /* spurious calls */ {0}, }; const struct ime_call set_conversion_status_2_seq[] = @@ -4046,6 +4054,10 @@ static void test_ImmSetConversionStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCONVERSIONMODE}, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCONVERSIONMODE}, @@ -4054,6 +4066,10 @@ static void test_ImmSetConversionStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xfeedcafe, .value = IMC_SETSENTENCEMODE}, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSENTENCEMODE}, @@ -4072,7 +4088,7 @@ static void test_ImmSetConversionStatus(void) ok_eq( old_conversion, ctx->fdwConversion, UINT, "%#x" ); ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); - hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); process_messages(); @@ -4126,6 +4142,7 @@ static void test_ImmSetConversionStatus(void) ok_eq( 0xdeadbeef, ctx->fdwConversion, UINT, "%#x" ); ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" ); + ctx->hWnd = 0; ok_seq( empty_sequence ); ok_ret( 1, ImmSetConversionStatus( default_himc, 0, 0xfeedcafe ) ); ok_seq( set_conversion_status_1_seq ); @@ -4136,6 +4153,7 @@ static void test_ImmSetConversionStatus(void) ok_eq( 0, ctx->fdwConversion, UINT, "%#x" ); ok_eq( 0xfeedcafe, ctx->fdwSentence, UINT, "%#x" ); + ctx->hWnd = hwnd; ok_seq( empty_sequence ); ok_ret( 1, ImmSetConversionStatus( default_himc, ~0, ~0 ) ); ok_seq( set_conversion_status_2_seq ); @@ -4192,6 +4210,10 @@ static void test_ImmSetOpenStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, @@ -4205,6 +4227,20 @@ static void test_ImmSetOpenStatus(void) .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, .todo = TRUE, }, + {0}, + }; + const struct ime_call set_open_status_2_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, + .todo = TRUE, + }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, @@ -4223,7 +4259,7 @@ static void test_ImmSetOpenStatus(void) ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); - hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); process_messages(); @@ -4268,9 +4304,18 @@ static void test_ImmSetOpenStatus(void) ok_eq( 0xdeadbeef, status, UINT, "%#x" ); ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + ctx->hWnd = 0; + ok_ret( 1, ImmSetOpenStatus( default_himc, 0xfeedcafe ) ); + ok_seq( set_open_status_1_seq ); + + status = ImmGetOpenStatus( default_himc ); + todo_wine ok_eq( 0xfeedcafe, status, UINT, "%#x" ); + todo_wine ok_eq( 0xfeedcafe, ctx->fOpen, UINT, "%#x" ); + + ctx->hWnd = hwnd; ok_seq( empty_sequence ); ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) ); - ok_seq( set_open_status_1_seq ); + ok_seq( set_open_status_2_seq ); status = ImmGetOpenStatus( default_himc ); todo_wine ok_eq( ~0, status, UINT, "%#x" ); From 5c376aa090e5ec6e8a10e4f20f7929a8414d292f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 22:39:49 +0200 Subject: [PATCH 226/758] imm32/tests: Add more ImmGetCompositionString(W|A) tests. (cherry picked from commit 272677e7e0f476b235eaf9bf3534cfe3fd6905fc) --- dlls/imm32/tests/imm32.c | 333 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index b064e44e578..fdc25fe4a3d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -91,6 +91,12 @@ extern BOOL WINAPI ImmActivateLayout(HKL); #define check_member( val, exp, fmt, member ) \ check_member_( __FILE__, __LINE__, val, exp, fmt, member ) +#define check_member_wstr_( file, line, val, exp, member ) \ + ok_(file, line)( !wcscmp( (val).member, (exp).member ), "got " #member " %s\n", \ + debugstr_w((val).member) ) +#define check_member_wstr( val, exp, member ) \ + check_member_wstr_( __FILE__, __LINE__, val, exp, member ) + #define check_member_point_( file, line, val, exp, member ) \ ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(POINT) ), \ "got " #member " %s\n", wine_dbgstr_point( &(val).member ) ) @@ -103,6 +109,36 @@ extern BOOL WINAPI ImmActivateLayout(HKL); #define check_member_rect( val, exp, member ) \ check_member_rect_( __FILE__, __LINE__, val, exp, member ) +#define check_composition_string( a, b ) check_composition_string_( __LINE__, a, b ) +static void check_composition_string_( int line, COMPOSITIONSTRING *string, const COMPOSITIONSTRING *expect ) +{ + check_member_( __FILE__, line, *string, *expect, "%lu", dwSize ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadAttrLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadAttrOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadClauseLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadClauseOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadStrLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompReadStrOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompAttrLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompAttrOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompClauseLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompClauseOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompStrLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCompStrOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwCursorPos ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwDeltaStart ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadClauseLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadClauseOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadStrLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultReadStrOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultClauseLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultClauseOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultStrLen ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwResultStrOffset ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwPrivateSize ); + check_member_( __FILE__, line, *string, *expect, "%lu", dwPrivateOffset ); +} + #define check_candidate_list( a, b ) check_candidate_list_( __LINE__, a, b, TRUE ) static void check_candidate_list_( int line, CANDIDATELIST *list, const CANDIDATELIST *expect, BOOL unicode ) { @@ -600,7 +636,7 @@ static LRESULT WINAPI test_ime_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPAR hWnd, msg, wParam, lParam); } -static void test_ImmGetCompositionString(void) +static void test_SCS_SETSTR(void) { HIMC imc; static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e}; @@ -5476,6 +5512,296 @@ static void test_ImmGetCandidateWindow(void) ime_call_count = 0; } +static void test_ImmGetCompositionString( BOOL unicode ) +{ + static COMPOSITIONSTRING expect_string_empty = {.dwSize = sizeof(COMPOSITIONSTRING)}; + static COMPOSITIONSTRING expect_stringA = + { + .dwSize = 176, + .dwCompReadAttrLen = 8, + .dwCompReadAttrOffset = 116, + .dwCompReadClauseLen = 8, + .dwCompReadClauseOffset = 108, + .dwCompReadStrLen = 8, + .dwCompReadStrOffset = 100, + .dwCompAttrLen = 4, + .dwCompAttrOffset = 136, + .dwCompClauseLen = 8, + .dwCompClauseOffset = 128, + .dwCompStrLen = 4, + .dwCompStrOffset = 124, + .dwCursorPos = 3, + .dwDeltaStart = 1, + .dwResultReadClauseLen = 8, + .dwResultReadClauseOffset = 150, + .dwResultReadStrLen = 10, + .dwResultReadStrOffset = 140, + .dwResultClauseLen = 8, + .dwResultClauseOffset = 164, + .dwResultStrLen = 6, + .dwResultStrOffset = 158, + .dwPrivateSize = 4, + .dwPrivateOffset = 172, + }; + static const COMPOSITIONSTRING expect_stringW = + { + .dwSize = 204, + .dwCompReadAttrLen = 8, + .dwCompReadAttrOffset = 124, + .dwCompReadClauseLen = 8, + .dwCompReadClauseOffset = 116, + .dwCompReadStrLen = 8, + .dwCompReadStrOffset = 100, + .dwCompAttrLen = 4, + .dwCompAttrOffset = 148, + .dwCompClauseLen = 8, + .dwCompClauseOffset = 140, + .dwCompStrLen = 4, + .dwCompStrOffset = 132, + .dwCursorPos = 3, + .dwDeltaStart = 1, + .dwResultReadClauseLen = 8, + .dwResultReadClauseOffset = 172, + .dwResultReadStrLen = 10, + .dwResultReadStrOffset = 152, + .dwResultClauseLen = 8, + .dwResultClauseOffset = 192, + .dwResultStrLen = 6, + .dwResultStrOffset = 180, + .dwPrivateSize = 4, + .dwPrivateOffset = 200, + }; + static const UINT gcs_indexes[] = + { + GCS_COMPREADSTR, + GCS_COMPREADATTR, + GCS_COMPREADCLAUSE, + GCS_COMPSTR, + GCS_COMPATTR, + GCS_COMPCLAUSE, + GCS_CURSORPOS, + GCS_DELTASTART, + GCS_RESULTREADSTR, + GCS_RESULTREADCLAUSE, + GCS_RESULTSTR, + GCS_RESULTCLAUSE, + }; + static const UINT expect_retW[ARRAY_SIZE(gcs_indexes)] = {16, 8, 8, 8, 4, 8, 3, 1, 20, 8, 12, 8}; + static const UINT expect_retA[ARRAY_SIZE(gcs_indexes)] = {8, 8, 8, 4, 4, 8, 3, 1, 10, 8, 6, 8}; + HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + COMPOSITIONSTRING *string; + char buffer[1024]; + INPUTCONTEXT *old_ctx, *ctx; + const void *str; + HIMCC old_himcc; + UINT i, len; + BYTE *dst; + HIMC himc; + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = ime_install())) goto cleanup; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + memset( buffer, 0xcd, sizeof(buffer) ); + todo_wine ok_ret( -2, ImmGetCompositionStringW( default_himc, GCS_COMPSTR | GCS_COMPATTR, buffer, sizeof(buffer) ) ); + memset( buffer, 0xcd, sizeof(buffer) ); + todo_wine ok_ret( -2, ImmGetCompositionStringA( default_himc, GCS_COMPSTR | GCS_COMPATTR, buffer, sizeof(buffer) ) ); + + for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i) + { + memset( buffer, 0xcd, sizeof(buffer) ); + ok_ret( 0, ImmGetCompositionStringW( default_himc, gcs_indexes[i], buffer, sizeof(buffer) ) ); + memset( buffer, 0xcd, sizeof(buffer) ); + ok_ret( 0, ImmGetCompositionStringA( default_himc, gcs_indexes[i], buffer, sizeof(buffer) ) ); + + memset( buffer, 0xcd, sizeof(buffer) ); + ok_ret( 0, ImmGetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer) ) ); + memset( buffer, 0xcd, sizeof(buffer) ); + ok_ret( 0, ImmGetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer) ) ); + } + + ctx->hCompStr = ImmReSizeIMCC( ctx->hCompStr, unicode ? expect_stringW.dwSize : expect_stringA.dwSize ); + string = ImmLockIMCC( ctx->hCompStr ); + ok( !!string, "ImmLockIMCC failed, error %lu\n", GetLastError() ); + check_composition_string( string, &expect_string_empty ); + + string->dwCursorPos = 3; + string->dwDeltaStart = 1; + + if (unicode) str = L"ReadComp"; + else str = "ReadComp"; + len = unicode ? wcslen( str ) : strlen( str ); + + string->dwCompReadStrLen = len; + string->dwCompReadStrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompReadStrOffset; + memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) ); + string->dwSize += len * (unicode ? sizeof(WCHAR) : 1); + + string->dwCompReadClauseLen = 2 * sizeof(DWORD); + string->dwCompReadClauseOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompReadClauseOffset; + *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0; + *(DWORD *)(dst + 1 * sizeof(DWORD)) = len; + string->dwSize += 2 * sizeof(DWORD); + + string->dwCompReadAttrLen = len; + string->dwCompReadAttrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompReadAttrOffset; + memset( dst, ATTR_INPUT, len ); + string->dwSize += len; + + if (unicode) str = L"Comp"; + else str = "Comp"; + len = unicode ? wcslen( str ) : strlen( str ); + + string->dwCompStrLen = len; + string->dwCompStrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompStrOffset; + memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) ); + string->dwSize += len * (unicode ? sizeof(WCHAR) : 1); + + string->dwCompClauseLen = 2 * sizeof(DWORD); + string->dwCompClauseOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompClauseOffset; + *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0; + *(DWORD *)(dst + 1 * sizeof(DWORD)) = len; + string->dwSize += 2 * sizeof(DWORD); + + string->dwCompAttrLen = len; + string->dwCompAttrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompAttrOffset; + memset( dst, ATTR_INPUT, len ); + string->dwSize += len; + + if (unicode) str = L"ReadResult"; + else str = "ReadResult"; + len = unicode ? wcslen( str ) : strlen( str ); + + string->dwResultReadStrLen = len; + string->dwResultReadStrOffset = string->dwSize; + dst = (BYTE *)string + string->dwResultReadStrOffset; + memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) ); + string->dwSize += len * (unicode ? sizeof(WCHAR) : 1); + + string->dwResultReadClauseLen = 2 * sizeof(DWORD); + string->dwResultReadClauseOffset = string->dwSize; + dst = (BYTE *)string + string->dwResultReadClauseOffset; + *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0; + *(DWORD *)(dst + 1 * sizeof(DWORD)) = len; + string->dwSize += 2 * sizeof(DWORD); + + if (unicode) str = L"Result"; + else str = "Result"; + len = unicode ? wcslen( str ) : strlen( str ); + + string->dwResultStrLen = len; + string->dwResultStrOffset = string->dwSize; + dst = (BYTE *)string + string->dwResultStrOffset; + memcpy( dst, str, len * (unicode ? sizeof(WCHAR) : 1) ); + string->dwSize += len * (unicode ? sizeof(WCHAR) : 1); + + string->dwResultClauseLen = 2 * sizeof(DWORD); + string->dwResultClauseOffset = string->dwSize; + dst = (BYTE *)string + string->dwResultClauseOffset; + *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0; + *(DWORD *)(dst + 1 * sizeof(DWORD)) = len; + string->dwSize += 2 * sizeof(DWORD); + + string->dwPrivateSize = 4; + string->dwPrivateOffset = string->dwSize; + dst = (BYTE *)string + string->dwPrivateOffset; + memset( dst, 0xa5, string->dwPrivateSize ); + string->dwSize += 4; + + check_composition_string( string, unicode ? &expect_stringW : &expect_stringA ); + ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) ); + old_himcc = ctx->hCompStr; + + for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i) + { + UINT_PTR expect; + + winetest_push_context( "%u", i ); + + memset( buffer, 0xcd, sizeof(buffer) ); + expect = expect_retW[i]; + ok_ret( expect, ImmGetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer) ) ); + memset( buffer + expect, 0, 4 ); + + if (i == 0) ok_wcs( L"ReadComp", (WCHAR *)buffer ); + else if (i == 3) ok_wcs( L"Comp", (WCHAR *)buffer ); + else if (i == 8) ok_wcs( L"ReadResult", (WCHAR *)buffer ); + else if (i == 10) ok_wcs( L"Result", (WCHAR *)buffer ); + else if (i != 6 && i != 7) ok_wcs( L"", (WCHAR *)buffer ); + + memset( buffer, 0xcd, sizeof(buffer) ); + expect = expect_retA[i]; + ok_ret( expect, ImmGetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer) ) ); + memset( buffer + expect, 0, 4 ); + + if (i == 0) ok_str( "ReadComp", (char *)buffer ); + else if (i == 3) ok_str( "Comp", (char *)buffer ); + else if (i == 8) ok_str( "ReadResult", (char *)buffer ); + else if (i == 10) ok_str( "Result", (char *)buffer ); + else if (i != 6 && i != 7) ok_str( "", (char *)buffer ); + + winetest_pop_context(); + } + ok_seq( empty_sequence ); + + old_ctx = ctx; + ok_ret( 1, ImmUnlockIMC( himc ) ); + + /* composition strings are kept between IME selections */ + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ctx = ImmLockIMC( himc ); + ok_eq( old_ctx, ctx, INPUTCONTEXT *, "%p" ); + ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" ); + string = ImmLockIMCC( ctx->hCompStr ); + ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" ); + *string = expect_string_empty; + ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) ); + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" ); + check_composition_string( string, &expect_string_empty ); + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" ); + check_composition_string( string, &expect_string_empty ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ime_cleanup( hkl, TRUE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + +cleanup: + winetest_pop_context(); +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -5528,10 +5854,13 @@ START_TEST(imm32) test_ImmGetCandidateListCount( FALSE ); test_ImmGetCandidateWindow(); + test_ImmGetCompositionString( TRUE ); + test_ImmGetCompositionString( FALSE ); + if (init()) { test_ImmNotifyIME(); - test_ImmGetCompositionString(); + test_SCS_SETSTR(); test_ImmSetCompositionString(); test_ImmIME(); test_ImmAssociateContextEx(); From 346d2bd0fd98dd36342610dc3a7cc9f21f4c8949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 13:28:34 +0200 Subject: [PATCH 227/758] imm32/tests: Add more ImmSetCompositionString tests. (cherry picked from commit 82971f23483e58e28e7ff3961d5348161b919321) --- dlls/imm32/tests/imm32.c | 176 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 9 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index fdc25fe4a3d..82809f37f8e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -778,12 +778,6 @@ static void test_SCS_SETSTR(void) skip("WM_IME_COMPOSITION(GCS_RESULTSTR) isn't tested\n"); msg_spy_flush_msgs(); } -} - -static void test_ImmSetCompositionString(void) -{ - HIMC imc; - BOOL ret; SetLastError(0xdeadbeef); imc = ImmGetContext(hwnd); @@ -2569,6 +2563,8 @@ DEFINE_EXPECT( ImeEnumRegisterWord ); DEFINE_EXPECT( ImeRegisterWord ); DEFINE_EXPECT( ImeGetRegisterWordStyle ); DEFINE_EXPECT( ImeUnregisterWord ); +static BOOL todo_ImeSetCompositionString; +DEFINE_EXPECT( ImeSetCompositionString ); static BOOL todo_IME_DLL_PROCESS_ATTACH; DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); static BOOL todo_IME_DLL_PROCESS_DETACH; @@ -3071,8 +3067,85 @@ static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const vo { ime_trace( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu\n", himc, index, comp, comp_len, read, read_len ); - ok( 0, "unexpected call\n" ); - return FALSE; + CHECK_EXPECT( ImeSetCompositionString ); + + ok_eq( expect_ime, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_ne( default_himc, himc, HIMC, "%p" ); + + if (ime_info.fdwProperty & IME_PROP_UNICODE) + { + switch (index) + { + case SCS_SETSTR: + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 22, comp_len, UINT, "%#x" ); + ok_wcs( L"CompString", comp ); + break; + case SCS_CHANGECLAUSE: + { + const UINT *clause = comp; + ok_eq( 8, comp_len, UINT, "%#x" ); + ok_eq( 0, clause[0], UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 1, clause[1], UINT, "%#x"); + break; + } + case SCS_CHANGEATTR: + { + const BYTE *attr = comp; + todo_wine_if( todo_ImeSetCompositionString && comp_len != 4 ) + ok_eq( 4, comp_len, UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString && attr[0] != 0xcd ) + ok_eq( 0xcd, attr[0], UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 0xcd, attr[1], UINT, "%#x" ); + break; + } + default: + ok( 0, "unexpected index %#lx\n", index ); + break; + } + } + else + { + switch (index) + { + case SCS_SETSTR: + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 11, comp_len, UINT, "%#x" ); + ok_str( "CompString", comp ); + break; + case SCS_CHANGECLAUSE: + { + const UINT *clause = comp; + ok_eq( 8, comp_len, UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 0, clause[0], UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 1, clause[1], UINT, "%#x"); + break; + } + case SCS_CHANGEATTR: + { + const BYTE *attr = comp; + todo_wine_if( todo_ImeSetCompositionString && comp_len != 4 ) + ok_eq( 4, comp_len, UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 0xcd, attr[0], UINT, "%#x" ); + todo_wine_if( todo_ImeSetCompositionString ) + ok_eq( 0xcd, attr[1], UINT, "%#x" ); + break; + } + default: + ok( 0, "unexpected index %#lx\n", index ); + break; + } + } + + ok_eq( NULL, read, const void *, "%p" ); + ok_eq( 0, read_len, UINT, "%#x" ); + + return TRUE; } static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) @@ -5586,6 +5659,12 @@ static void test_ImmGetCompositionString( BOOL unicode ) GCS_RESULTSTR, GCS_RESULTCLAUSE, }; + static const UINT scs_indexes[] = + { + SCS_SETSTR, + SCS_CHANGEATTR, + SCS_CHANGECLAUSE, + }; static const UINT expect_retW[ARRAY_SIZE(gcs_indexes)] = {16, 8, 8, 8, 4, 8, 3, 1, 20, 8, 12, 8}; static const UINT expect_retA[ARRAY_SIZE(gcs_indexes)] = {8, 8, 8, 4, 4, 8, 3, 1, 10, 8, 6, 8}; HKL hkl, old_hkl = GetKeyboardLayout( 0 ); @@ -5598,6 +5677,8 @@ static void test_ImmGetCompositionString( BOOL unicode ) BYTE *dst; HIMC himc; + SET_ENABLE( ImeSetCompositionString, TRUE ); + winetest_push_context( unicode ? "unicode" : "ansi" ); /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ @@ -5766,6 +5847,83 @@ static void test_ImmGetCompositionString( BOOL unicode ) winetest_pop_context(); } + + for (i = 0; i < ARRAY_SIZE(gcs_indexes); ++i) + { + winetest_push_context( "%u", i ); + ok_ret( 0, ImmSetCompositionStringW( himc, gcs_indexes[i], buffer, sizeof(buffer), NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringA( himc, gcs_indexes[i], buffer, sizeof(buffer), NULL, 0 ) ); + winetest_pop_context(); + } + ok_ret( 0, ImmSetCompositionStringW( himc, SCS_SETSTR | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringA( himc, SCS_SETSTR | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringW( himc, SCS_CHANGECLAUSE | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringA( himc, SCS_CHANGECLAUSE | SCS_CHANGEATTR, buffer, sizeof(buffer), NULL, 0 ) ); + + for (i = 0; i < ARRAY_SIZE(scs_indexes); ++i) + { + winetest_push_context( "%u", i ); + + if (scs_indexes[i] == SCS_CHANGECLAUSE) + { + memset( buffer, 0, sizeof(buffer) ); + *((DWORD *)buffer + 1) = 1; + len = 2 * sizeof(DWORD); + } + else if (scs_indexes[i] == SCS_CHANGEATTR) + { + memset( buffer, 0xcd, sizeof(buffer) ); + len = expect_stringW.dwCompAttrLen; + } + else if (scs_indexes[i] == SCS_SETSTR) + { + wcscpy( (WCHAR *)buffer, L"CompString" ); + len = 11 * sizeof(WCHAR); + } + + todo_ImeSetCompositionString = !unicode; + SET_EXPECT( ImeSetCompositionString ); + ok_ret( 1, ImmSetCompositionStringW( himc, scs_indexes[i], buffer, len, NULL, 0 ) ); + CHECK_CALLED( ImeSetCompositionString ); + todo_ImeSetCompositionString = FALSE; + ok_seq( empty_sequence ); + + string = ImmLockIMCC( ctx->hCompStr ); + ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" ); + check_composition_string( string, unicode ? &expect_stringW : &expect_stringA ); + ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) ); + + if (scs_indexes[i] == SCS_CHANGECLAUSE) + { + memset( buffer, 0, sizeof(buffer) ); + *((DWORD *)buffer + 1) = 1; + len = 2 * sizeof(DWORD); + } + else if (scs_indexes[i] == SCS_CHANGEATTR) + { + memset( buffer, 0xcd, sizeof(buffer) ); + len = expect_stringA.dwCompAttrLen; + } + else if (scs_indexes[i] == SCS_SETSTR) + { + strcpy( buffer, "CompString" ); + len = 11; + } + + todo_ImeSetCompositionString = unicode; + SET_EXPECT( ImeSetCompositionString ); + ok_ret( 1, ImmSetCompositionStringA( himc, scs_indexes[i], buffer, len, NULL, 0 ) ); + CHECK_CALLED( ImeSetCompositionString ); + todo_ImeSetCompositionString = FALSE; + ok_seq( empty_sequence ); + + string = ImmLockIMCC( ctx->hCompStr ); + ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" ); + check_composition_string( string, unicode ? &expect_stringW : &expect_stringA ); + ok_ret( 0, ImmUnlockIMCC( ctx->hCompStr ) ); + + winetest_pop_context(); + } ok_seq( empty_sequence ); old_ctx = ctx; @@ -5800,6 +5958,7 @@ static void test_ImmGetCompositionString( BOOL unicode ) cleanup: winetest_pop_context(); + SET_ENABLE( ImeSetCompositionString, FALSE ); } START_TEST(imm32) @@ -5861,7 +6020,6 @@ START_TEST(imm32) { test_ImmNotifyIME(); test_SCS_SETSTR(); - test_ImmSetCompositionString(); test_ImmIME(); test_ImmAssociateContextEx(); test_NtUserAssociateInputContext(); From ff6dcb582cc407b76e2efc40a8e8fee81af2953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:25:36 +0200 Subject: [PATCH 228/758] winex11: Cleanup spaces in IME UI window proc. (cherry picked from commit a5117ed5cd93b54645285c89694e6069102405aa) --- dlls/winex11.drv/ime.c | 267 ++++++++++++++++++----------------------- 1 file changed, 115 insertions(+), 152 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index a499c8d9dd6..931289b751b 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -998,26 +998,25 @@ NTSTATUS WINAPI x11drv_ime_set_result( void *params, ULONG len ) /***** * Internal functions to help with IME window management */ -static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) +static void PaintDefaultIMEWnd( HIMC hIMC, HWND hwnd ) { PAINTSTRUCT ps; RECT rect; HDC hdc; HMONITOR monitor; MONITORINFO mon_info; - INT offX=0, offY=0; + INT offX = 0, offY = 0; LPINPUTCONTEXT lpIMC; WCHAR *str; UINT len; - lpIMC = ImmLockIMC(hIMC); - if (lpIMC == NULL) - return; + lpIMC = ImmLockIMC( hIMC ); + if (lpIMC == NULL) return; - hdc = BeginPaint(hwnd,&ps); + hdc = BeginPaint( hwnd, &ps ); - GetClientRect(hwnd,&rect); - FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1)); + GetClientRect( hwnd, &rect ); + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { @@ -1028,7 +1027,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) GetTextExtentPoint32W( hdc, str, len, &size ); pt.x = size.cx; pt.y = size.cy; - LPtoDP(hdc,&pt,1); + LPtoDP( hdc, &pt, 1 ); /* * How this works based on tests on windows: @@ -1045,12 +1044,12 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT) { POINT cpt = lpIMC->cfCompForm.ptCurrentPos; - ClientToScreen(lpIMC->hWnd,&cpt); + ClientToScreen( lpIMC->hWnd, &cpt ); rect.left = cpt.x; rect.top = cpt.y; rect.right = rect.left + pt.x; rect.bottom = rect.top + pt.y; - monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY); + monitor = MonitorFromPoint( cpt, MONITOR_DEFAULTTOPRIMARY ); } else /* CFS_DEFAULT */ { @@ -1058,20 +1057,20 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) HWND target = lpIMC->hWnd; if (!target) target = GetFocus(); - GetWindowRect(target,&rect); + GetWindowRect( target, &rect ); rect.top = rect.bottom; rect.right = rect.left + pt.x + 20; rect.bottom = rect.top + pt.y + 20; - offX=offY=10; - monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY); + offX = offY = 10; + monitor = MonitorFromWindow( target, MONITOR_DEFAULTTOPRIMARY ); } if (lpIMC->cfCompForm.dwStyle == CFS_RECT) { RECT client; - client =lpIMC->cfCompForm.rcArea; + client = lpIMC->cfCompForm.rcArea; MapWindowPoints( lpIMC->hWnd, 0, (POINT *)&client, 2 ); - IntersectRect(&rect,&rect,&client); + IntersectRect( &rect, &rect, &client ); /* TODO: Wrap the input if needed */ } @@ -1079,7 +1078,7 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) { /* make sure we are on the desktop */ mon_info.cbSize = sizeof(mon_info); - GetMonitorInfoW(monitor, &mon_info); + GetMonitorInfoW( monitor, &mon_info ); if (rect.bottom > mon_info.rcWork.bottom) { @@ -1100,118 +1099,85 @@ static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) } } - SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE); - + SetWindowPos( hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOACTIVATE ); TextOutW( hdc, offX, offY, str, len ); if (font) SelectObject( hdc, font ); free( str ); } - EndPaint(hwnd,&ps); - ImmUnlockIMC(hIMC); + EndPaint( hwnd, &ps ); + ImmUnlockIMC( hIMC ); } -static void UpdateDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd) +static void UpdateDefaultIMEWindow( INPUTCONTEXT *lpIMC, HWND hwnd ) { LPCOMPOSITIONSTRING compstr; - if (lpIMC->hCompStr) - compstr = ImmLockIMCC(lpIMC->hCompStr); - else - compstr = NULL; + if (lpIMC->hCompStr) compstr = ImmLockIMCC( lpIMC->hCompStr ); + else compstr = NULL; if (compstr == NULL || compstr->dwCompStrLen == 0) - ShowWindow(hwnd,SW_HIDE); + ShowWindow( hwnd, SW_HIDE ); else { - ShowWindow(hwnd,SW_SHOWNOACTIVATE); - RedrawWindow(hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE); + ShowWindow( hwnd, SW_SHOWNOACTIVATE ); + RedrawWindow( hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE ); } - if (compstr != NULL) - ImmUnlockIMCC(lpIMC->hCompStr); + if (compstr != NULL) ImmUnlockIMCC( lpIMC->hCompStr ); lpIMC->hWnd = GetFocus(); } -static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) +static void DefaultIMEComposition( HIMC hIMC, HWND hwnd, LPARAM lParam ) { INPUTCONTEXT *ctx; - TRACE("IME message WM_IME_COMPOSITION 0x%Ix\n", lParam); + TRACE( "IME message WM_IME_COMPOSITION 0x%Ix\n", lParam ); if (lParam & GCS_RESULTSTR) return; if (!(ctx = ImmLockIMC( hIMC ))) return; UpdateDefaultIMEWindow( ctx, hwnd ); - ImmUnlockIMC(hIMC); + ImmUnlockIMC( hIMC ); } -static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd ) +static void DefaultIMEStartComposition( HIMC hIMC, HWND hwnd ) { INPUTCONTEXT *ctx; - TRACE("IME message WM_IME_STARTCOMPOSITION\n"); + TRACE( "IME message WM_IME_STARTCOMPOSITION\n" ); if (!(ctx = ImmLockIMC( hIMC ))) return; UpdateDefaultIMEWindow( ctx, hwnd ); - ImmUnlockIMC(hIMC); + ImmUnlockIMC( hIMC ); } -static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, - LPARAM lParam) +static LRESULT ImeHandleNotify( HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch (wParam) { - case IMN_OPENSTATUSWINDOW: - FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n"); - break; - case IMN_CLOSESTATUSWINDOW: - FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n"); - break; - case IMN_OPENCANDIDATE: - FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n"); - break; - case IMN_CHANGECANDIDATE: - FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n"); - break; - case IMN_CLOSECANDIDATE: - FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n"); - break; - case IMN_SETCONVERSIONMODE: - FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n"); - break; - case IMN_SETSENTENCEMODE: - FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n"); - break; - case IMN_SETOPENSTATUS: - TRACE("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n"); - break; - case IMN_SETCANDIDATEPOS: - FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n"); - break; - case IMN_SETCOMPOSITIONFONT: - FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n"); - break; - case IMN_SETCOMPOSITIONWINDOW: - FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n"); - break; - case IMN_GUIDELINE: - FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n"); - break; - case IMN_SETSTATUSWINDOWPOS: - FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n"); - break; - default: - FIXME("WM_IME_NOTIFY:\n",wParam); - break; + case IMN_OPENSTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n" ); break; + case IMN_CLOSESTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n" ); break; + case IMN_OPENCANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_OPENCANDIDATE\n" ); break; + case IMN_CHANGECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n" ); break; + case IMN_CLOSECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n" ); break; + case IMN_SETCONVERSIONMODE: FIXME( "WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n" ); break; + case IMN_SETSENTENCEMODE: FIXME( "WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n" ); break; + case IMN_SETOPENSTATUS: TRACE( "WM_IME_NOTIFY:IMN_SETOPENSTATUS\n" ); break; + case IMN_SETCANDIDATEPOS: FIXME( "WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n" ); break; + case IMN_SETCOMPOSITIONFONT: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n" ); break; + case IMN_SETCOMPOSITIONWINDOW: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n" ); break; + case IMN_GUIDELINE: FIXME( "WM_IME_NOTIFY:IMN_GUIDELINE\n" ); break; + case IMN_SETSTATUSWINDOWPOS: FIXME( "WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n" ); break; + default: FIXME( "WM_IME_NOTIFY:\n", wParam ); break; } return 0; } -static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, - LPARAM lParam) +static LRESULT WINAPI IME_WindowProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { LRESULT rc = 0; - HIMC hIMC; + HIMC hIMC; - TRACE("Incoming Message 0x%x (0x%08Ix, 0x%08Ix)\n", msg, wParam, lParam); + TRACE( "Incoming Message 0x%x (0x%08Ix, 0x%08Ix)\n", msg, wParam, lParam ); /* * Each UI window contains the current Input Context. @@ -1221,12 +1187,13 @@ static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, * messages. */ - hIMC = (HIMC)GetWindowLongPtrW(hwnd,IMMGWL_IMC); + hIMC = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); /* if we have no hIMC there are many messages we cannot process */ if (hIMC == NULL) { - switch (msg) { + switch (msg) + { case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION: case WM_IME_COMPOSITION: @@ -1234,104 +1201,100 @@ static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_IME_CONTROL: case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: - case WM_IME_CHAR: - return 0L; - default: - break; + case WM_IME_CHAR: return 0L; + default: break; } } - switch(msg) + switch (msg) { - case WM_CREATE: - { - LPIMEPRIVATE myPrivate; - LPINPUTCONTEXT lpIMC; - - SetWindowTextA(hwnd,"Wine Ime Active"); + case WM_CREATE: + { + LPIMEPRIVATE myPrivate; + LPINPUTCONTEXT lpIMC; - lpIMC = ImmLockIMC(hIMC); - if (lpIMC) - { - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - myPrivate->hwndDefault = hwnd; - ImmUnlockIMCC(lpIMC->hPrivate); - } - ImmUnlockIMC(hIMC); + SetWindowTextA( hwnd, "Wine Ime Active" ); - return TRUE; + lpIMC = ImmLockIMC( hIMC ); + if (lpIMC) + { + myPrivate = ImmLockIMCC( lpIMC->hPrivate ); + myPrivate->hwndDefault = hwnd; + ImmUnlockIMCC( lpIMC->hPrivate ); } - case WM_PAINT: - PaintDefaultIMEWnd(hIMC, hwnd); - return FALSE; - - case WM_NCCREATE: - return TRUE; + ImmUnlockIMC( hIMC ); - case WM_SETFOCUS: - if (wParam) - SetFocus((HWND)wParam); - else - FIXME("Received focus, should never have focus\n"); - break; - case WM_IME_COMPOSITION: - DefaultIMEComposition(hIMC, hwnd, lParam); - break; - case WM_IME_STARTCOMPOSITION: - DefaultIMEStartComposition(hIMC, hwnd); - break; - case WM_IME_ENDCOMPOSITION: - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", - "WM_IME_ENDCOMPOSITION", wParam, lParam); - ShowWindow(hwnd,SW_HIDE); - break; - case WM_IME_SELECT: - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_IME_SELECT", wParam, lParam); - break; - case WM_IME_CONTROL: - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_IME_CONTROL", wParam, lParam); - rc = 1; - break; - case WM_IME_NOTIFY: - rc = ImeHandleNotify(hIMC,hwnd,msg,wParam,lParam); - break; - default: - TRACE("Non-standard message 0x%x\n",msg); + return TRUE; + } + case WM_PAINT: + PaintDefaultIMEWnd( hIMC, hwnd ); + return FALSE; + case WM_NCCREATE: + return TRUE; + case WM_SETFOCUS: + if (wParam) SetFocus( (HWND)wParam ); + else FIXME( "Received focus, should never have focus\n" ); + break; + + case WM_IME_COMPOSITION: + DefaultIMEComposition( hIMC, hwnd, lParam ); + break; + case WM_IME_STARTCOMPOSITION: + DefaultIMEStartComposition( hIMC, hwnd ); + break; + case WM_IME_ENDCOMPOSITION: + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_ENDCOMPOSITION", wParam, lParam ); + ShowWindow( hwnd, SW_HIDE ); + break; + case WM_IME_SELECT: + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_SELECT", wParam, lParam ); + break; + case WM_IME_CONTROL: + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_CONTROL", wParam, lParam ); + rc = 1; + break; + case WM_IME_NOTIFY: + rc = ImeHandleNotify( hIMC, hwnd, msg, wParam, lParam ); + break; + default: + TRACE( "Non-standard message 0x%x\n", msg ); } + /* check the MSIME messages */ if (msg == WM_MSIME_SERVICE) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_SERVICE", wParam, lParam); - rc = FALSE; + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_SERVICE", wParam, lParam ); + rc = FALSE; } else if (msg == WM_MSIME_RECONVERTOPTIONS) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_RECONVERTOPTIONS", wParam, lParam); + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTOPTIONS", wParam, lParam ); } else if (msg == WM_MSIME_MOUSE) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_MOUSE", wParam, lParam); + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_MOUSE", wParam, lParam ); } else if (msg == WM_MSIME_RECONVERTREQUEST) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_RECONVERTREQUEST", wParam, lParam); + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTREQUEST", wParam, lParam ); } else if (msg == WM_MSIME_RECONVERT) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_RECONVERT", wParam, lParam); + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERT", wParam, lParam ); } else if (msg == WM_MSIME_QUERYPOSITION) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_QUERYPOSITION", wParam, lParam); + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_QUERYPOSITION", wParam, lParam ); } else if (msg == WM_MSIME_DOCUMENTFEED) { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n","WM_MSIME_DOCUMENTFEED", wParam, lParam); + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_DOCUMENTFEED", wParam, lParam ); } + /* DefWndProc if not an IME message */ if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || - (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) - rc = DefWindowProcW(hwnd,msg,wParam,lParam); + (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) + rc = DefWindowProcW( hwnd, msg, wParam, lParam ); return rc; } From e2e7a7bee47dc7c037bf0ebd9df0a8b6d3f42fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:59:00 +0200 Subject: [PATCH 229/758] winex11: Move IME UI proc to default IME implementation. (cherry picked from commit bc1b15211d4a68b9dfebd870ad2fc4ffb8be8f78) --- dlls/imm32/ime.c | 351 ++++++++++++++++++++++++-- dlls/imm32/imm.c | 71 +++--- dlls/imm32/imm_private.h | 46 ++++ dlls/winemac.drv/ime.c | 10 - dlls/winex11.drv/ime.c | 393 ------------------------------ dlls/winex11.drv/winex11.drv.spec | 2 - include/ntuser.h | 10 + 7 files changed, 422 insertions(+), 461 deletions(-) create mode 100644 dlls/imm32/imm_private.h diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index c05ca6d0255..f48a0a861d1 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -19,29 +19,352 @@ #include #include -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "imm.h" -#include "immdev.h" - -#include "wine/debug.h" +#include "imm_private.h" WINE_DEFAULT_DEBUG_CHANNEL(imm); +static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) +{ + COMPOSITIONSTRING *string; + WCHAR *text = NULL; + UINT len, off; + + if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL; + len = result ? string->dwResultStrLen : string->dwCompStrLen; + off = result ? string->dwResultStrOffset : string->dwCompStrOffset; + + if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) ))) + { + memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) ); + text[len] = 0; + *length = len; + } + + ImmUnlockIMCC( ctx->hCompStr ); + return text; +} + +static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) +{ + struct ime_private *priv; + HFONT font = NULL; + if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; + if (priv->textfont) font = SelectObject( hdc, priv->textfont ); + ImmUnlockIMCC( ctx->hPrivate ); + return font; +} + +static void ime_ui_paint( HIMC himc, HWND hwnd ) +{ + PAINTSTRUCT ps; + RECT rect; + HDC hdc; + HMONITOR monitor; + MONITORINFO mon_info; + INPUTCONTEXT *ctx; + POINT offset; + WCHAR *str; + UINT len; + + if (!(ctx = ImmLockIMC( himc ))) return; + + hdc = BeginPaint( hwnd, &ps ); + + GetClientRect( hwnd, &rect ); + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + if ((str = input_context_get_comp_str( ctx, FALSE, &len ))) + { + HFONT font = input_context_select_ui_font( ctx, hdc ); + SIZE size; + POINT pt; + + GetTextExtentPoint32W( hdc, str, len, &size ); + pt.x = size.cx; + pt.y = size.cy; + LPtoDP( hdc, &pt, 1 ); + + /* + * How this works based on tests on windows: + * CFS_POINT: then we start our window at the point and grow it as large + * as it needs to be for the string. + * CFS_RECT: we still use the ptCurrentPos as a starting point and our + * window is only as large as we need for the string, but we do not + * grow such that our window exceeds the given rect. Wrapping if + * needed and possible. If our ptCurrentPos is outside of our rect + * then no window is displayed. + * CFS_FORCE_POSITION: appears to behave just like CFS_POINT + * maybe because the default MSIME does not do any IME adjusting. + */ + if (ctx->cfCompForm.dwStyle != CFS_DEFAULT) + { + POINT cpt = ctx->cfCompForm.ptCurrentPos; + ClientToScreen( ctx->hWnd, &cpt ); + rect.left = cpt.x; + rect.top = cpt.y; + rect.right = rect.left + pt.x; + rect.bottom = rect.top + pt.y; + monitor = MonitorFromPoint( cpt, MONITOR_DEFAULTTOPRIMARY ); + } + else /* CFS_DEFAULT */ + { + /* Windows places the default IME window in the bottom left */ + HWND target = ctx->hWnd; + if (!target) target = GetFocus(); + + GetWindowRect( target, &rect ); + rect.top = rect.bottom; + rect.right = rect.left + pt.x + 20; + rect.bottom = rect.top + pt.y + 20; + offset.x = offset.y = 10; + monitor = MonitorFromWindow( target, MONITOR_DEFAULTTOPRIMARY ); + } + + if (ctx->cfCompForm.dwStyle == CFS_RECT) + { + RECT client; + client = ctx->cfCompForm.rcArea; + MapWindowPoints( ctx->hWnd, 0, (POINT *)&client, 2 ); + IntersectRect( &rect, &rect, &client ); + /* TODO: Wrap the input if needed */ + } + + if (ctx->cfCompForm.dwStyle == CFS_DEFAULT) + { + /* make sure we are on the desktop */ + mon_info.cbSize = sizeof(mon_info); + GetMonitorInfoW( monitor, &mon_info ); + + if (rect.bottom > mon_info.rcWork.bottom) + { + int shift = rect.bottom - mon_info.rcWork.bottom; + rect.top -= shift; + rect.bottom -= shift; + } + if (rect.left < 0) + { + rect.right -= rect.left; + rect.left = 0; + } + if (rect.right > mon_info.rcWork.right) + { + int shift = rect.right - mon_info.rcWork.right; + rect.left -= shift; + rect.right -= shift; + } + } + + SetWindowPos( hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOACTIVATE ); + TextOutW( hdc, offset.x, offset.y, str, len ); + + if (font) SelectObject( hdc, font ); + free( str ); + } + + EndPaint( hwnd, &ps ); + ImmUnlockIMC( himc ); +} + +static void ime_ui_update_window( INPUTCONTEXT *ctx, HWND hwnd ) +{ + COMPOSITIONSTRING *string; + + if (ctx->hCompStr) string = ImmLockIMCC( ctx->hCompStr ); + else string = NULL; + + if (!string || string->dwCompStrLen == 0) + ShowWindow( hwnd, SW_HIDE ); + else + { + ShowWindow( hwnd, SW_SHOWNOACTIVATE ); + RedrawWindow( hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE ); + } + + if (string) ImmUnlockIMCC( ctx->hCompStr ); + + ctx->hWnd = GetFocus(); +} + +static void ime_ui_composition( HIMC himc, HWND hwnd, LPARAM lparam ) +{ + INPUTCONTEXT *ctx; + TRACE( "IME message WM_IME_COMPOSITION 0x%Ix\n", lparam ); + if (lparam & GCS_RESULTSTR) return; + if (!(ctx = ImmLockIMC( himc ))) return; + ime_ui_update_window( ctx, hwnd ); + ImmUnlockIMC( himc ); +} + +static void ime_ui_start_composition( HIMC himc, HWND hwnd ) +{ + INPUTCONTEXT *ctx; + TRACE( "IME message WM_IME_STARTCOMPOSITION\n" ); + if (!(ctx = ImmLockIMC( himc ))) return; + ime_ui_update_window( ctx, hwnd ); + ImmUnlockIMC( himc ); +} + +static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + switch (wparam) + { + case IMN_OPENSTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n" ); break; + case IMN_CLOSESTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n" ); break; + case IMN_OPENCANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_OPENCANDIDATE\n" ); break; + case IMN_CHANGECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n" ); break; + case IMN_CLOSECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n" ); break; + case IMN_SETCONVERSIONMODE: FIXME( "WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n" ); break; + case IMN_SETSENTENCEMODE: FIXME( "WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n" ); break; + case IMN_SETOPENSTATUS: TRACE( "WM_IME_NOTIFY:IMN_SETOPENSTATUS\n" ); break; + case IMN_SETCANDIDATEPOS: FIXME( "WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n" ); break; + case IMN_SETCOMPOSITIONFONT: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n" ); break; + case IMN_SETCOMPOSITIONWINDOW: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n" ); break; + case IMN_GUIDELINE: FIXME( "WM_IME_NOTIFY:IMN_GUIDELINE\n" ); break; + case IMN_SETSTATUSWINDOWPOS: FIXME( "WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n" ); break; + default: FIXME( "WM_IME_NOTIFY:\n", wparam ); break; + } + return 0; +} + +static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); + INPUTCONTEXT *ctx; + LRESULT ret = 0; + + TRACE( "hwnd %p, himc %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, himc, msg, wparam, lparam ); + + /* if we have no himc there are many messages we cannot process */ + if (!himc) + { + switch (msg) + { + case WM_IME_STARTCOMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_COMPOSITION: + case WM_IME_NOTIFY: + case WM_IME_CONTROL: + case WM_IME_COMPOSITIONFULL: + case WM_IME_SELECT: + case WM_IME_CHAR: return 0L; + default: break; + } + } + + switch (msg) + { + case WM_CREATE: + { + struct ime_private *priv; + + SetWindowTextA( hwnd, "Wine Ime Active" ); + + if (!(ctx = ImmLockIMC( himc ))) return TRUE; + if ((priv = ImmLockIMCC( ctx->hPrivate ))) + { + priv->hwndDefault = hwnd; + ImmUnlockIMCC( ctx->hPrivate ); + } + ImmUnlockIMC( himc ); + return TRUE; + } + case WM_PAINT: + ime_ui_paint( himc, hwnd ); + return FALSE; + case WM_NCCREATE: + return TRUE; + case WM_SETFOCUS: + if (wparam) SetFocus( (HWND)wparam ); + else FIXME( "Received focus, should never have focus\n" ); + break; + case WM_IME_COMPOSITION: + ime_ui_composition( himc, hwnd, lparam ); + break; + case WM_IME_STARTCOMPOSITION: + ime_ui_start_composition( himc, hwnd ); + break; + case WM_IME_ENDCOMPOSITION: + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_ENDCOMPOSITION", wparam, lparam ); + ShowWindow( hwnd, SW_HIDE ); + break; + case WM_IME_SELECT: + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_SELECT", wparam, lparam ); + break; + case WM_IME_CONTROL: + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_CONTROL", wparam, lparam ); + ret = 1; + break; + case WM_IME_NOTIFY: + ret = ime_ui_notify( himc, hwnd, msg, wparam, lparam ); + break; + default: + TRACE( "Non-standard message 0x%x\n", msg ); + break; + } + + /* check the MSIME messages */ + if (msg == WM_MSIME_SERVICE) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_SERVICE", wparam, lparam ); + else if (msg == WM_MSIME_RECONVERTOPTIONS) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTOPTIONS", wparam, lparam ); + else if (msg == WM_MSIME_MOUSE) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_MOUSE", wparam, lparam ); + else if (msg == WM_MSIME_RECONVERTREQUEST) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTREQUEST", wparam, lparam ); + else if (msg == WM_MSIME_RECONVERT) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERT", wparam, lparam ); + else if (msg == WM_MSIME_QUERYPOSITION) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_QUERYPOSITION", wparam, lparam ); + else if (msg == WM_MSIME_DOCUMENTFEED) + TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_DOCUMENTFEED", wparam, lparam ); + + /* DefWndProc if not an IME message */ + if (!ret && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || + (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) + ret = DefWindowProcW( hwnd, msg, wparam, lparam ); + + return ret; +} + +static WNDCLASSEXW ime_ui_class = +{ + .cbSize = sizeof(WNDCLASSEXW), + .style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW, + .lpfnWndProc = ime_ui_window_proc, + .cbWndExtra = 2 * sizeof(LONG_PTR), + .lpszClassName = L"Wine IME", + .hbrBackground = (HBRUSH)(COLOR_WINDOW + 1), +}; + BOOL WINAPI ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) { - FIXME( "info %p, ui_class %p, flags %#lx stub!\n", info, ui_class, flags ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + TRACE( "info %p, ui_class %p, flags %#lx\n", info, ui_class, flags ); + + ime_ui_class.hInstance = imm32_module; + ime_ui_class.hCursor = LoadCursorW( NULL, (LPWSTR)IDC_ARROW ); + ime_ui_class.hIcon = LoadIconW( NULL, (LPWSTR)IDI_APPLICATION ); + RegisterClassExW( &ime_ui_class ); + + wcscpy( ui_class, ime_ui_class.lpszClassName ); + memset( info, 0, sizeof(*info) ); + info->dwPrivateDataSize = sizeof(IMEPRIVATE); + info->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; + info->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE; + info->fdwSentenceCaps = IME_SMODE_AUTOMATIC; + info->fdwUICaps = UI_CAP_2700; + /* Tell App we cannot accept ImeSetCompositionString calls */ + info->fdwSCSCaps = 0; + info->fdwSelectCaps = SELECT_CAP_CONVERSION; + + return TRUE; } BOOL WINAPI ImeDestroy( UINT force ) { - FIXME( "force %u stub!\n", force ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + TRACE( "force %u\n", force ); + UnregisterClassW( ime_ui_class.lpszClassName, imm32_module ); + return TRUE; } BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index e936bb27754..8eee8604bcf 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -20,37 +20,24 @@ */ #define COBJMACROS - -#include -#include - #include "initguid.h" -#include "objbase.h" -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "ntuser.h" -#include "winerror.h" -#include "wine/debug.h" -#include "imm.h" -#include "immdev.h" -#include "winnls.h" -#include "winreg.h" -#include "wine/list.h" +#include "imm_private.h" WINE_DEFAULT_DEBUG_CHANNEL(imm); #define IMM_INIT_MAGIC 0x19650412 BOOL WINAPI User32InitializeImmEntryTable(DWORD); +HMODULE imm32_module; + /* MSIME messages */ -static UINT WM_MSIME_SERVICE; -static UINT WM_MSIME_RECONVERTOPTIONS; -static UINT WM_MSIME_MOUSE; -static UINT WM_MSIME_RECONVERTREQUEST; -static UINT WM_MSIME_RECONVERT; -static UINT WM_MSIME_QUERYPOSITION; -static UINT WM_MSIME_DOCUMENTFEED; +UINT WM_MSIME_SERVICE; +UINT WM_MSIME_RECONVERTOPTIONS; +UINT WM_MSIME_MOUSE; +UINT WM_MSIME_RECONVERTREQUEST; +UINT WM_MSIME_RECONVERT; +UINT WM_MSIME_QUERYPOSITION; +UINT WM_MSIME_DOCUMENTFEED; struct ime { @@ -709,28 +696,28 @@ static void IMM_FreeAllImmHkl(void) } } -BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) { - TRACE("%p, %lx, %p\n",hInstDLL,fdwReason,lpReserved); - switch (fdwReason) + TRACE( "instance %p, reason %lx, reserved %p\n", instance, reason, reserved ); + + switch (reason) { - case DLL_PROCESS_ATTACH: - if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC)) - { - return FALSE; - } - break; - case DLL_THREAD_ATTACH: - break; - case DLL_THREAD_DETACH: - IMM_FreeThreadData(); - break; - case DLL_PROCESS_DETACH: - if (lpReserved) break; - IMM_FreeThreadData(); - IMM_FreeAllImmHkl(); - break; + case DLL_PROCESS_ATTACH: + if (!User32InitializeImmEntryTable( IMM_INIT_MAGIC )) return FALSE; + imm32_module = instance; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + IMM_FreeThreadData(); + break; + case DLL_PROCESS_DETACH: + if (reserved) break; + IMM_FreeThreadData(); + IMM_FreeAllImmHkl(); + break; } + return TRUE; } diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h new file mode 100644 index 00000000000..57b496ef434 --- /dev/null +++ b/dlls/imm32/imm_private.h @@ -0,0 +1,46 @@ +/* + * Copyright 2023 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 +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" + +#include "imm.h" +#include "immdev.h" +#include "ntuser.h" +#include "objbase.h" + +#include "wine/debug.h" +#include "wine/list.h" + +extern HMODULE imm32_module; + +/* MSIME messages */ +extern UINT WM_MSIME_SERVICE; +extern UINT WM_MSIME_RECONVERTOPTIONS; +extern UINT WM_MSIME_MOUSE; +extern UINT WM_MSIME_RECONVERTREQUEST; +extern UINT WM_MSIME_RECONVERT; +extern UINT WM_MSIME_QUERYPOSITION; +extern UINT WM_MSIME_DOCUMENTFEED; diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 5e78ae055fb..1d04092cae0 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -44,16 +44,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); #define FROM_MACDRV ((HIMC)0xcafe1337) -typedef struct ime_private -{ - BOOL bInComposition; - BOOL bInternalState; - HFONT textfont; - HWND hwndDefault; - - UINT repeat; -} IMEPRIVATE, *LPIMEPRIVATE; - static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e',' ','M','a','c',' ','I','M','E',0}; static HIMC *hSelectedFrom = NULL; diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 931289b751b..55485bfbfcf 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -49,28 +49,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); #define FROM_X11 ((HIMC)0xcafe1337) -typedef struct ime_private -{ - BOOL bInComposition; - BOOL bInternalState; - HFONT textfont; - HWND hwndDefault; -} IMEPRIVATE, *LPIMEPRIVATE; - -static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e','X','1','1','I','M','E',0}; - static HIMC *hSelectedFrom = NULL; static INT hSelectedCount = 0; -/* MSIME messages */ -static UINT WM_MSIME_SERVICE; -static UINT WM_MSIME_RECONVERTOPTIONS; -static UINT WM_MSIME_MOUSE; -static UINT WM_MSIME_RECONVERTREQUEST; -static UINT WM_MSIME_RECONVERT; -static UINT WM_MSIME_QUERYPOSITION; -static UINT WM_MSIME_DOCUMENTFEED; - static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) { COMPOSITIONSTRING *string; @@ -92,19 +73,6 @@ static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT * return text; } -static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) -{ - struct ime_private *priv; - HFONT font = NULL; - if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; - if (priv->textfont) font = SelectObject( hdc, priv->textfont ); - ImmUnlockIMCC( ctx->hPrivate ); - return font; -} - -static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam); - static HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_X11) @@ -139,34 +107,6 @@ static BOOL UnlockRealIMC(HIMC hIMC) return FALSE; } -static BOOL WINAPI register_classes( INIT_ONCE *once, void *param, void **context ) -{ - WNDCLASSW wndClass; - - ZeroMemory(&wndClass, sizeof(WNDCLASSW)); - wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = IME_WindowProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 2 * sizeof(LONG_PTR); - wndClass.hInstance = x11drv_module; - wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW); - wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION); - wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW +1); - wndClass.lpszMenuName = 0; - wndClass.lpszClassName = UI_CLASS_NAME; - - RegisterClassW(&wndClass); - - WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService"); - WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions"); - WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation"); - WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest"); - WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert"); - WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition"); - WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed"); - return TRUE; -} - static HIMCC ImeCreateBlankCompStr(void) { HIMCC rc; @@ -537,35 +477,6 @@ static void IME_AddToSelected(HIMC hIMC) hSelectedFrom[hSelectedCount-1] = hIMC; } -BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, DWORD flags) -{ - static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; - - TRACE("\n"); - InitOnceExecuteOnce( &init_once, register_classes, NULL, NULL ); - lpIMEInfo->dwPrivateDataSize = sizeof (IMEPRIVATE); - lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; - lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE; - lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC; - lpIMEInfo->fdwUICaps = UI_CAP_2700; - /* Tell App we cannot accept ImeSetCompositionString calls */ - lpIMEInfo->fdwSCSCaps = 0; - lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; - - lstrcpyW(lpszUIClass,UI_CLASS_NAME); - - return TRUE; -} - -BOOL WINAPI ImeDestroy(UINT uForce) -{ - TRACE("\n"); - HeapFree(GetProcessHeap(),0,hSelectedFrom); - hSelectedFrom = NULL; - hSelectedCount = 0; - return TRUE; -} - BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) { /* See the comment at the head of this file */ @@ -994,307 +905,3 @@ NTSTATUS WINAPI x11drv_ime_set_result( void *params, ULONG len ) ImmUnlockIMC(imc); return 0; } - -/***** - * Internal functions to help with IME window management - */ -static void PaintDefaultIMEWnd( HIMC hIMC, HWND hwnd ) -{ - PAINTSTRUCT ps; - RECT rect; - HDC hdc; - HMONITOR monitor; - MONITORINFO mon_info; - INT offX = 0, offY = 0; - LPINPUTCONTEXT lpIMC; - WCHAR *str; - UINT len; - - lpIMC = ImmLockIMC( hIMC ); - if (lpIMC == NULL) return; - - hdc = BeginPaint( hwnd, &ps ); - - GetClientRect( hwnd, &rect ); - FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); - - if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) - { - HFONT font = input_context_select_ui_font( lpIMC, hdc ); - SIZE size; - POINT pt; - - GetTextExtentPoint32W( hdc, str, len, &size ); - pt.x = size.cx; - pt.y = size.cy; - LPtoDP( hdc, &pt, 1 ); - - /* - * How this works based on tests on windows: - * CFS_POINT: then we start our window at the point and grow it as large - * as it needs to be for the string. - * CFS_RECT: we still use the ptCurrentPos as a starting point and our - * window is only as large as we need for the string, but we do not - * grow such that our window exceeds the given rect. Wrapping if - * needed and possible. If our ptCurrentPos is outside of our rect - * then no window is displayed. - * CFS_FORCE_POSITION: appears to behave just like CFS_POINT - * maybe because the default MSIME does not do any IME adjusting. - */ - if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT) - { - POINT cpt = lpIMC->cfCompForm.ptCurrentPos; - ClientToScreen( lpIMC->hWnd, &cpt ); - rect.left = cpt.x; - rect.top = cpt.y; - rect.right = rect.left + pt.x; - rect.bottom = rect.top + pt.y; - monitor = MonitorFromPoint( cpt, MONITOR_DEFAULTTOPRIMARY ); - } - else /* CFS_DEFAULT */ - { - /* Windows places the default IME window in the bottom left */ - HWND target = lpIMC->hWnd; - if (!target) target = GetFocus(); - - GetWindowRect( target, &rect ); - rect.top = rect.bottom; - rect.right = rect.left + pt.x + 20; - rect.bottom = rect.top + pt.y + 20; - offX = offY = 10; - monitor = MonitorFromWindow( target, MONITOR_DEFAULTTOPRIMARY ); - } - - if (lpIMC->cfCompForm.dwStyle == CFS_RECT) - { - RECT client; - client = lpIMC->cfCompForm.rcArea; - MapWindowPoints( lpIMC->hWnd, 0, (POINT *)&client, 2 ); - IntersectRect( &rect, &rect, &client ); - /* TODO: Wrap the input if needed */ - } - - if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT) - { - /* make sure we are on the desktop */ - mon_info.cbSize = sizeof(mon_info); - GetMonitorInfoW( monitor, &mon_info ); - - if (rect.bottom > mon_info.rcWork.bottom) - { - int shift = rect.bottom - mon_info.rcWork.bottom; - rect.top -= shift; - rect.bottom -= shift; - } - if (rect.left < 0) - { - rect.right -= rect.left; - rect.left = 0; - } - if (rect.right > mon_info.rcWork.right) - { - int shift = rect.right - mon_info.rcWork.right; - rect.left -= shift; - rect.right -= shift; - } - } - - SetWindowPos( hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, SWP_NOACTIVATE ); - TextOutW( hdc, offX, offY, str, len ); - - if (font) SelectObject( hdc, font ); - free( str ); - } - - EndPaint( hwnd, &ps ); - ImmUnlockIMC( hIMC ); -} - -static void UpdateDefaultIMEWindow( INPUTCONTEXT *lpIMC, HWND hwnd ) -{ - LPCOMPOSITIONSTRING compstr; - - if (lpIMC->hCompStr) compstr = ImmLockIMCC( lpIMC->hCompStr ); - else compstr = NULL; - - if (compstr == NULL || compstr->dwCompStrLen == 0) - ShowWindow( hwnd, SW_HIDE ); - else - { - ShowWindow( hwnd, SW_SHOWNOACTIVATE ); - RedrawWindow( hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE ); - } - - if (compstr != NULL) ImmUnlockIMCC( lpIMC->hCompStr ); - - lpIMC->hWnd = GetFocus(); -} - -static void DefaultIMEComposition( HIMC hIMC, HWND hwnd, LPARAM lParam ) -{ - INPUTCONTEXT *ctx; - TRACE( "IME message WM_IME_COMPOSITION 0x%Ix\n", lParam ); - if (lParam & GCS_RESULTSTR) return; - if (!(ctx = ImmLockIMC( hIMC ))) return; - UpdateDefaultIMEWindow( ctx, hwnd ); - ImmUnlockIMC( hIMC ); -} - -static void DefaultIMEStartComposition( HIMC hIMC, HWND hwnd ) -{ - INPUTCONTEXT *ctx; - TRACE( "IME message WM_IME_STARTCOMPOSITION\n" ); - if (!(ctx = ImmLockIMC( hIMC ))) return; - UpdateDefaultIMEWindow( ctx, hwnd ); - ImmUnlockIMC( hIMC ); -} - -static LRESULT ImeHandleNotify( HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) -{ - switch (wParam) - { - case IMN_OPENSTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n" ); break; - case IMN_CLOSESTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n" ); break; - case IMN_OPENCANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_OPENCANDIDATE\n" ); break; - case IMN_CHANGECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n" ); break; - case IMN_CLOSECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n" ); break; - case IMN_SETCONVERSIONMODE: FIXME( "WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n" ); break; - case IMN_SETSENTENCEMODE: FIXME( "WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n" ); break; - case IMN_SETOPENSTATUS: TRACE( "WM_IME_NOTIFY:IMN_SETOPENSTATUS\n" ); break; - case IMN_SETCANDIDATEPOS: FIXME( "WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n" ); break; - case IMN_SETCOMPOSITIONFONT: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n" ); break; - case IMN_SETCOMPOSITIONWINDOW: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n" ); break; - case IMN_GUIDELINE: FIXME( "WM_IME_NOTIFY:IMN_GUIDELINE\n" ); break; - case IMN_SETSTATUSWINDOWPOS: FIXME( "WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n" ); break; - default: FIXME( "WM_IME_NOTIFY:\n", wParam ); break; - } - return 0; -} - -static LRESULT WINAPI IME_WindowProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) -{ - LRESULT rc = 0; - HIMC hIMC; - - TRACE( "Incoming Message 0x%x (0x%08Ix, 0x%08Ix)\n", msg, wParam, lParam ); - - /* - * Each UI window contains the current Input Context. - * This Input Context can be obtained by calling GetWindowLong - * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message. - * The UI window can refer to this Input Context and handles the - * messages. - */ - - hIMC = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); - - /* if we have no hIMC there are many messages we cannot process */ - if (hIMC == NULL) - { - switch (msg) - { - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_IME_NOTIFY: - case WM_IME_CONTROL: - case WM_IME_COMPOSITIONFULL: - case WM_IME_SELECT: - case WM_IME_CHAR: return 0L; - default: break; - } - } - - switch (msg) - { - case WM_CREATE: - { - LPIMEPRIVATE myPrivate; - LPINPUTCONTEXT lpIMC; - - SetWindowTextA( hwnd, "Wine Ime Active" ); - - lpIMC = ImmLockIMC( hIMC ); - if (lpIMC) - { - myPrivate = ImmLockIMCC( lpIMC->hPrivate ); - myPrivate->hwndDefault = hwnd; - ImmUnlockIMCC( lpIMC->hPrivate ); - } - ImmUnlockIMC( hIMC ); - - return TRUE; - } - case WM_PAINT: - PaintDefaultIMEWnd( hIMC, hwnd ); - return FALSE; - case WM_NCCREATE: - return TRUE; - case WM_SETFOCUS: - if (wParam) SetFocus( (HWND)wParam ); - else FIXME( "Received focus, should never have focus\n" ); - break; - - case WM_IME_COMPOSITION: - DefaultIMEComposition( hIMC, hwnd, lParam ); - break; - case WM_IME_STARTCOMPOSITION: - DefaultIMEStartComposition( hIMC, hwnd ); - break; - case WM_IME_ENDCOMPOSITION: - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_ENDCOMPOSITION", wParam, lParam ); - ShowWindow( hwnd, SW_HIDE ); - break; - case WM_IME_SELECT: - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_SELECT", wParam, lParam ); - break; - case WM_IME_CONTROL: - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_CONTROL", wParam, lParam ); - rc = 1; - break; - case WM_IME_NOTIFY: - rc = ImeHandleNotify( hIMC, hwnd, msg, wParam, lParam ); - break; - default: - TRACE( "Non-standard message 0x%x\n", msg ); - } - - /* check the MSIME messages */ - if (msg == WM_MSIME_SERVICE) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_SERVICE", wParam, lParam ); - rc = FALSE; - } - else if (msg == WM_MSIME_RECONVERTOPTIONS) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTOPTIONS", wParam, lParam ); - } - else if (msg == WM_MSIME_MOUSE) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_MOUSE", wParam, lParam ); - } - else if (msg == WM_MSIME_RECONVERTREQUEST) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTREQUEST", wParam, lParam ); - } - else if (msg == WM_MSIME_RECONVERT) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERT", wParam, lParam ); - } - else if (msg == WM_MSIME_QUERYPOSITION) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_QUERYPOSITION", wParam, lParam ); - } - else if (msg == WM_MSIME_DOCUMENTFEED) - { - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_DOCUMENTFEED", wParam, lParam ); - } - - /* DefWndProc if not an IME message */ - if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || - (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) - rc = DefWindowProcW( hwnd, msg, wParam, lParam ); - - return rc; -} diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index 0596d48c577..a753536eb3f 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -11,8 +11,6 @@ @ cdecl wine_notify_icon(long ptr) #IME Interface -@ stdcall ImeInquire(ptr ptr wstr) -@ stdcall ImeDestroy(long) @ stdcall ImeSelect(long long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) @ stdcall NotifyIME(long long long long) diff --git a/include/ntuser.h b/include/ntuser.h index 86267d3e244..67f6cd54837 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -490,6 +490,16 @@ enum wine_internal_message #define IME_INTERNAL_HKL_ACTIVATE 0x19 #define IME_INTERNAL_HKL_DEACTIVATE 0x20 +/* internal IME private */ +typedef struct ime_private +{ + BOOL bInComposition; + BOOL bInternalState; + HFONT textfont; + HWND hwndDefault; + UINT repeat; +} IMEPRIVATE, *LPIMEPRIVATE; + #define WM_SYSTIMER 0x0118 /* the various structures that can be sent in messages, in platform-independent layout */ From 7ac093474a1ed705e373fb7a56ba3a0ff5318d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Apr 2023 16:21:46 +0200 Subject: [PATCH 230/758] winemac: Use the default IME UI window proc implementation. This slightly changes ime_ui_update_window to match what was previously in winex11.drv, since e5f0cdfcf6ba1595346c4c9ed1a7cbf3de418c8e, which seems to be a more recent change. (cherry picked from commit d8fa43377d895f92149e8fc1872a8b22261b33a2) --- dlls/winemac.drv/ime.c | 391 ------------------------------ dlls/winemac.drv/winemac.drv.spec | 2 - 2 files changed, 393 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 1d04092cae0..bca8377d146 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -44,20 +44,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); #define FROM_MACDRV ((HIMC)0xcafe1337) -static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e',' ','M','a','c',' ','I','M','E',0}; - static HIMC *hSelectedFrom = NULL; static INT hSelectedCount = 0; -/* MSIME messages */ -static UINT WM_MSIME_SERVICE; -static UINT WM_MSIME_RECONVERTOPTIONS; -static UINT WM_MSIME_MOUSE; -static UINT WM_MSIME_RECONVERTREQUEST; -static UINT WM_MSIME_RECONVERT; -static UINT WM_MSIME_QUERYPOSITION; -static UINT WM_MSIME_DOCUMENTFEED; - static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) { COMPOSITIONSTRING *string; @@ -545,15 +534,6 @@ static void UpdateDataInDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd, BOOL sh ImmUnlockIMCC(lpIMC->hCompStr); } -BOOL WINAPI ImeDestroy(UINT uForce) -{ - TRACE("\n"); - HeapFree(GetProcessHeap(), 0, hSelectedFrom); - hSelectedFrom = NULL; - hSelectedCount = 0; - return TRUE; -} - BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) { LPINPUTCONTEXT lpIMC; @@ -950,377 +930,6 @@ static void IME_NotifyComplete(void* hIMC) NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); } -/***** - * Internal functions to help with IME window management - */ -static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) -{ - PAINTSTRUCT ps; - RECT rect; - HDC hdc; - HMONITOR monitor; - MONITORINFO mon_info; - INT offX = 0, offY = 0; - LPINPUTCONTEXT lpIMC; - WCHAR *str; - UINT len; - - lpIMC = ImmLockIMC(hIMC); - if (lpIMC == NULL) - return; - - hdc = BeginPaint(hwnd, &ps); - - GetClientRect(hwnd, &rect); - FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1)); - - if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) - { - HFONT font = input_context_select_ui_font( lpIMC, hdc ); - SIZE size; - POINT pt; - - GetTextExtentPoint32W( hdc, str, len, &size ); - pt.x = size.cx; - pt.y = size.cy; - LPtoDP(hdc, &pt, 1); - - /* - * How this works based on tests on windows: - * CFS_POINT: then we start our window at the point and grow it as large - * as it needs to be for the string. - * CFS_RECT: we still use the ptCurrentPos as a starting point and our - * window is only as large as we need for the string, but we do not - * grow such that our window exceeds the given rect. Wrapping if - * needed and possible. If our ptCurrentPos is outside of our rect - * then no window is displayed. - * CFS_FORCE_POSITION: appears to behave just like CFS_POINT - * maybe because the default MSIME does not do any IME adjusting. - */ - if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT) - { - POINT cpt = lpIMC->cfCompForm.ptCurrentPos; - ClientToScreen(lpIMC->hWnd, &cpt); - rect.left = cpt.x; - rect.top = cpt.y; - rect.right = rect.left + pt.x; - rect.bottom = rect.top + pt.y; - monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY); - } - else /* CFS_DEFAULT */ - { - /* Windows places the default IME window in the bottom left */ - HWND target = lpIMC->hWnd; - if (!target) target = GetFocus(); - - GetWindowRect(target, &rect); - rect.top = rect.bottom; - rect.right = rect.left + pt.x + 20; - rect.bottom = rect.top + pt.y + 20; - offX=offY=10; - monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY); - } - - if (lpIMC->cfCompForm.dwStyle == CFS_RECT) - { - RECT client; - client =lpIMC->cfCompForm.rcArea; - MapWindowPoints(lpIMC->hWnd, 0, (POINT *)&client, 2); - IntersectRect(&rect, &rect, &client); - /* TODO: Wrap the input if needed */ - } - - if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT) - { - /* make sure we are on the desktop */ - mon_info.cbSize = sizeof(mon_info); - GetMonitorInfoW(monitor, &mon_info); - - if (rect.bottom > mon_info.rcWork.bottom) - { - int shift = rect.bottom - mon_info.rcWork.bottom; - rect.top -= shift; - rect.bottom -= shift; - } - if (rect.left < 0) - { - rect.right -= rect.left; - rect.left = 0; - } - if (rect.right > mon_info.rcWork.right) - { - int shift = rect.right - mon_info.rcWork.right; - rect.left -= shift; - rect.right -= shift; - } - } - - SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, SWP_NOACTIVATE); - - TextOutW( hdc, offX, offY, str, len ); - - if (font) SelectObject( hdc, font ); - free( str ); - } - - ImmUnlockIMCC(lpIMC->hCompStr); - - EndPaint(hwnd, &ps); - ImmUnlockIMC(hIMC); -} - -static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) -{ - INPUTCONTEXT *ctx; - TRACE("IME message WM_IME_COMPOSITION 0x%Ix\n", lParam); - if (lParam & GCS_RESULTSTR) return; - if (!(ctx = ImmLockIMC( hIMC ))) return; - UpdateDataInDefaultIMEWindow( ctx, hwnd, TRUE ); - ImmUnlockIMC(hIMC); -} - -static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd) -{ - LPINPUTCONTEXT lpIMC; - - lpIMC = ImmLockIMC(hIMC); - if (lpIMC == NULL) - return; - - TRACE("IME message WM_IME_STARTCOMPOSITION\n"); - lpIMC->hWnd = GetFocus(); - ShowWindow(hwnd, SW_SHOWNOACTIVATE); - ImmUnlockIMC(hIMC); -} - -static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (wParam) - { - case IMN_OPENSTATUSWINDOW: - FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n"); - break; - case IMN_CLOSESTATUSWINDOW: - FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n"); - break; - case IMN_OPENCANDIDATE: - FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n"); - break; - case IMN_CHANGECANDIDATE: - FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n"); - break; - case IMN_CLOSECANDIDATE: - FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n"); - break; - case IMN_SETCONVERSIONMODE: - FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n"); - break; - case IMN_SETSENTENCEMODE: - FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n"); - break; - case IMN_SETOPENSTATUS: - FIXME("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n"); - break; - case IMN_SETCANDIDATEPOS: - FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n"); - break; - case IMN_SETCOMPOSITIONFONT: - FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n"); - break; - case IMN_SETCOMPOSITIONWINDOW: - FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n"); - break; - case IMN_GUIDELINE: - FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n"); - break; - case IMN_SETSTATUSWINDOWPOS: - FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n"); - break; - default: - FIXME("WM_IME_NOTIFY:\n", wParam); - break; - } - return 0; -} - -static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - LRESULT rc = 0; - HIMC hIMC; - - TRACE("Incoming Message 0x%x (0x%08Ix, 0x%08Ix)\n", msg, wParam, lParam); - - /* - * Each UI window contains the current Input Context. - * This Input Context can be obtained by calling GetWindowLong - * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message. - * The UI window can refer to this Input Context and handles the - * messages. - */ - - hIMC = (HIMC)GetWindowLongPtrW(hwnd, IMMGWL_IMC); - - /* if we have no hIMC there are many messages we cannot process */ - if (hIMC == NULL) - { - switch (msg) { - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_IME_NOTIFY: - case WM_IME_CONTROL: - case WM_IME_COMPOSITIONFULL: - case WM_IME_SELECT: - case WM_IME_CHAR: - return 0L; - default: - break; - } - } - - switch (msg) - { - case WM_CREATE: - { - LPIMEPRIVATE myPrivate; - LPINPUTCONTEXT lpIMC; - - SetWindowTextA(hwnd, "Wine Ime Active"); - - lpIMC = ImmLockIMC(hIMC); - if (lpIMC) - { - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - myPrivate->hwndDefault = hwnd; - ImmUnlockIMCC(lpIMC->hPrivate); - } - ImmUnlockIMC(hIMC); - - return TRUE; - } - case WM_PAINT: - PaintDefaultIMEWnd(hIMC, hwnd); - return FALSE; - - case WM_NCCREATE: - return TRUE; - - case WM_SETFOCUS: - if (wParam) - SetFocus((HWND)wParam); - else - FIXME("Received focus, should never have focus\n"); - break; - case WM_IME_COMPOSITION: - DefaultIMEComposition(hIMC, hwnd, lParam); - break; - case WM_IME_STARTCOMPOSITION: - DefaultIMEStartComposition(hIMC, hwnd); - break; - case WM_IME_ENDCOMPOSITION: - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_ENDCOMPOSITION", wParam, lParam); - ShowWindow(hwnd, SW_HIDE); - break; - case WM_IME_SELECT: - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_SELECT", wParam, lParam); - break; - case WM_IME_CONTROL: - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_CONTROL", wParam, lParam); - rc = 1; - break; - case WM_IME_NOTIFY: - rc = ImeHandleNotify(hIMC, hwnd, msg, wParam, lParam); - break; - default: - TRACE("Non-standard message 0x%x\n", msg); - } - /* check the MSIME messages */ - if (msg == WM_MSIME_SERVICE) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_SERVICE", wParam, lParam); - rc = FALSE; - } - else if (msg == WM_MSIME_RECONVERTOPTIONS) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTOPTIONS", wParam, lParam); - } - else if (msg == WM_MSIME_MOUSE) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_MOUSE", wParam, lParam); - } - else if (msg == WM_MSIME_RECONVERTREQUEST) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTREQUEST", wParam, lParam); - } - else if (msg == WM_MSIME_RECONVERT) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERT", wParam, lParam); - } - else if (msg == WM_MSIME_QUERYPOSITION) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_QUERYPOSITION", wParam, lParam); - } - else if (msg == WM_MSIME_DOCUMENTFEED) - { - TRACE("IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_DOCUMENTFEED", wParam, lParam); - } - /* DefWndProc if not an IME message */ - if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || - (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) - rc = DefWindowProcW(hwnd, msg, wParam, lParam); - - return rc; -} - -static BOOL WINAPI register_classes( INIT_ONCE *once, void *param, void **context ) -{ - WNDCLASSW wndClass; - ZeroMemory(&wndClass, sizeof(WNDCLASSW)); - wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = (WNDPROC) IME_WindowProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 2 * sizeof(LONG_PTR); - wndClass.hInstance = macdrv_module; - wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW); - wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION); - wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wndClass.lpszMenuName = 0; - wndClass.lpszClassName = UI_CLASS_NAME; - - RegisterClassW(&wndClass); - - WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService"); - WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions"); - WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation"); - WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest"); - WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert"); - WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition"); - WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed"); - return TRUE; -} - -BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, DWORD flags) -{ - static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; - - TRACE("\n"); - InitOnceExecuteOnce( &init_once, register_classes, NULL, NULL ); - lpIMEInfo->dwPrivateDataSize = sizeof(IMEPRIVATE); - lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; - lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE; - lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC; - lpIMEInfo->fdwUICaps = UI_CAP_2700; - /* Tell App we cannot accept ImeSetCompositionString calls */ - /* FIXME: Can we? */ - lpIMEInfo->fdwSCSCaps = 0; - lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; - - lstrcpyW(lpszUIClass, UI_CLASS_NAME); - - return TRUE; -} - /* Interfaces to other parts of the Mac driver */ /*********************************************************************** diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index d5e94b53ce4..debaec8239d 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -2,8 +2,6 @@ @ cdecl wine_notify_icon(long ptr) # IME -@ stdcall ImeDestroy(long) -@ stdcall ImeInquire(ptr wstr wstr) @ stdcall ImeProcessKey(long long long ptr) @ stdcall ImeSelect(long long) @ stdcall ImeSetCompositionString(long long ptr long ptr long) From 1052886605f47e33649599fdab44374a00eeec9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 00:19:42 +0200 Subject: [PATCH 231/758] imm32: Cleanup default IME UI window proc traces. (cherry picked from commit c902be6a84c78c6b7788920525793ebadbaef186) --- dlls/imm32/ime.c | 102 ++++++++++++++++++--------------------- dlls/imm32/imm.c | 3 ++ dlls/imm32/imm_private.h | 28 +++++++++++ 3 files changed, 79 insertions(+), 54 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index f48a0a861d1..42f19b7cb2b 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -23,6 +23,45 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); +static const char *debugstr_imn( WPARAM wparam ) +{ + switch (wparam) + { + case IMN_OPENSTATUSWINDOW: return "IMN_OPENSTATUSWINDOW"; + case IMN_CLOSESTATUSWINDOW: return "IMN_CLOSESTATUSWINDOW"; + case IMN_OPENCANDIDATE: return "IMN_OPENCANDIDATE"; + case IMN_CHANGECANDIDATE: return "IMN_CHANGECANDIDATE"; + case IMN_CLOSECANDIDATE: return "IMN_CLOSECANDIDATE"; + case IMN_SETCONVERSIONMODE: return "IMN_SETCONVERSIONMODE"; + case IMN_SETSENTENCEMODE: return "IMN_SETSENTENCEMODE"; + case IMN_SETOPENSTATUS: return "IMN_SETOPENSTATUS"; + case IMN_SETCANDIDATEPOS: return "IMN_SETCANDIDATEPOS"; + case IMN_SETCOMPOSITIONFONT: return "IMN_SETCOMPOSITIONFONT"; + case IMN_SETCOMPOSITIONWINDOW: return "IMN_SETCOMPOSITIONWINDOW"; + case IMN_GUIDELINE: return "IMN_GUIDELINE"; + case IMN_SETSTATUSWINDOWPOS: return "IMN_SETSTATUSWINDOWPOS"; + default: return wine_dbg_sprintf( "%#Ix", wparam ); + } +} + +static const char *debugstr_imc( WPARAM wparam ) +{ + switch (wparam) + { + case IMC_GETCANDIDATEPOS: return "IMC_GETCANDIDATEPOS"; + case IMC_SETCANDIDATEPOS: return "IMC_SETCANDIDATEPOS"; + case IMC_GETCOMPOSITIONFONT: return "IMC_GETCOMPOSITIONFONT"; + case IMC_SETCOMPOSITIONFONT: return "IMC_SETCOMPOSITIONFONT"; + case IMC_GETCOMPOSITIONWINDOW: return "IMC_GETCOMPOSITIONWINDOW"; + case IMC_SETCOMPOSITIONWINDOW: return "IMC_SETCOMPOSITIONWINDOW"; + case IMC_GETSTATUSWINDOWPOS: return "IMC_GETSTATUSWINDOWPOS"; + case IMC_SETSTATUSWINDOWPOS: return "IMC_SETSTATUSWINDOWPOS"; + case IMC_CLOSESTATUSWINDOW: return "IMC_CLOSESTATUSWINDOW"; + case IMC_OPENSTATUSWINDOW: return "IMC_OPENSTATUSWINDOW"; + default: return wine_dbg_sprintf( "%#Ix", wparam ); + } +} + static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) { COMPOSITIONSTRING *string; @@ -189,7 +228,6 @@ static void ime_ui_update_window( INPUTCONTEXT *ctx, HWND hwnd ) static void ime_ui_composition( HIMC himc, HWND hwnd, LPARAM lparam ) { INPUTCONTEXT *ctx; - TRACE( "IME message WM_IME_COMPOSITION 0x%Ix\n", lparam ); if (lparam & GCS_RESULTSTR) return; if (!(ctx = ImmLockIMC( himc ))) return; ime_ui_update_window( ctx, hwnd ); @@ -199,41 +237,19 @@ static void ime_ui_composition( HIMC himc, HWND hwnd, LPARAM lparam ) static void ime_ui_start_composition( HIMC himc, HWND hwnd ) { INPUTCONTEXT *ctx; - TRACE( "IME message WM_IME_STARTCOMPOSITION\n" ); if (!(ctx = ImmLockIMC( himc ))) return; ime_ui_update_window( ctx, hwnd ); ImmUnlockIMC( himc ); } -static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) -{ - switch (wparam) - { - case IMN_OPENSTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n" ); break; - case IMN_CLOSESTATUSWINDOW: FIXME( "WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n" ); break; - case IMN_OPENCANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_OPENCANDIDATE\n" ); break; - case IMN_CHANGECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n" ); break; - case IMN_CLOSECANDIDATE: FIXME( "WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n" ); break; - case IMN_SETCONVERSIONMODE: FIXME( "WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n" ); break; - case IMN_SETSENTENCEMODE: FIXME( "WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n" ); break; - case IMN_SETOPENSTATUS: TRACE( "WM_IME_NOTIFY:IMN_SETOPENSTATUS\n" ); break; - case IMN_SETCANDIDATEPOS: FIXME( "WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n" ); break; - case IMN_SETCOMPOSITIONFONT: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n" ); break; - case IMN_SETCOMPOSITIONWINDOW: FIXME( "WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n" ); break; - case IMN_GUIDELINE: FIXME( "WM_IME_NOTIFY:IMN_GUIDELINE\n" ); break; - case IMN_SETSTATUSWINDOWPOS: FIXME( "WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n" ); break; - default: FIXME( "WM_IME_NOTIFY:\n", wparam ); break; - } - return 0; -} - static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); INPUTCONTEXT *ctx; LRESULT ret = 0; - TRACE( "hwnd %p, himc %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, himc, msg, wparam, lparam ); + TRACE( "hwnd %p, himc %p, msg %s, wparam %#Ix, lparam %#Ix\n", + hwnd, himc, debugstr_wm_ime(msg), wparam, lparam ); /* if we have no himc there are many messages we cannot process */ if (!himc) @@ -285,40 +301,18 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP ime_ui_start_composition( himc, hwnd ); break; case WM_IME_ENDCOMPOSITION: - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_ENDCOMPOSITION", wparam, lparam ); ShowWindow( hwnd, SW_HIDE ); break; - case WM_IME_SELECT: - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_SELECT", wparam, lparam ); - break; - case WM_IME_CONTROL: - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_IME_CONTROL", wparam, lparam ); - ret = 1; - break; case WM_IME_NOTIFY: - ret = ime_ui_notify( himc, hwnd, msg, wparam, lparam ); - break; - default: - TRACE( "Non-standard message 0x%x\n", msg ); - break; + FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc, + debugstr_wm_ime(msg), debugstr_imn(wparam), lparam ); + return 0; + case WM_IME_CONTROL: + FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc, + debugstr_wm_ime(msg), debugstr_imc(wparam), lparam ); + return 1; } - /* check the MSIME messages */ - if (msg == WM_MSIME_SERVICE) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_SERVICE", wparam, lparam ); - else if (msg == WM_MSIME_RECONVERTOPTIONS) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTOPTIONS", wparam, lparam ); - else if (msg == WM_MSIME_MOUSE) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_MOUSE", wparam, lparam ); - else if (msg == WM_MSIME_RECONVERTREQUEST) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERTREQUEST", wparam, lparam ); - else if (msg == WM_MSIME_RECONVERT) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_RECONVERT", wparam, lparam ); - else if (msg == WM_MSIME_QUERYPOSITION) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_QUERYPOSITION", wparam, lparam ); - else if (msg == WM_MSIME_DOCUMENTFEED) - TRACE( "IME message %s, 0x%Ix, 0x%Ix\n", "WM_MSIME_DOCUMENTFEED", wparam, lparam ); - /* DefWndProc if not an IME message */ if (!ret && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 8eee8604bcf..25d1df47a1c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3203,6 +3203,9 @@ LRESULT WINAPI __wine_ime_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp { HWND ui_hwnd; + TRACE( "hwnd %p, msg %s, wparam %#Ix, lparam %#Ix, ansi %u\n", + hwnd, debugstr_wm_ime(msg), wparam, lparam, ansi ); + switch (msg) { case WM_CREATE: diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h index 57b496ef434..8a957494ce9 100644 --- a/dlls/imm32/imm_private.h +++ b/dlls/imm32/imm_private.h @@ -44,3 +44,31 @@ extern UINT WM_MSIME_RECONVERTREQUEST; extern UINT WM_MSIME_RECONVERT; extern UINT WM_MSIME_QUERYPOSITION; extern UINT WM_MSIME_DOCUMENTFEED; + +static const char *debugstr_wm_ime( UINT msg ) +{ + switch (msg) + { + case WM_IME_STARTCOMPOSITION: return "WM_IME_STARTCOMPOSITION"; + case WM_IME_ENDCOMPOSITION: return "WM_IME_ENDCOMPOSITION"; + case WM_IME_COMPOSITION: return "WM_IME_COMPOSITION"; + case WM_IME_SETCONTEXT: return "WM_IME_SETCONTEXT"; + case WM_IME_NOTIFY: return "WM_IME_NOTIFY"; + case WM_IME_CONTROL: return "WM_IME_CONTROL"; + case WM_IME_COMPOSITIONFULL: return "WM_IME_COMPOSITIONFULL"; + case WM_IME_SELECT: return "WM_IME_SELECT"; + case WM_IME_CHAR: return "WM_IME_CHAR"; + case WM_IME_REQUEST: return "WM_IME_REQUEST"; + case WM_IME_KEYDOWN: return "WM_IME_KEYDOWN"; + case WM_IME_KEYUP: return "WM_IME_KEYUP"; + default: + if (msg == WM_MSIME_SERVICE) return "WM_MSIME_SERVICE"; + else if (msg == WM_MSIME_RECONVERTOPTIONS) return "WM_MSIME_RECONVERTOPTIONS"; + else if (msg == WM_MSIME_MOUSE) return "WM_MSIME_MOUSE"; + else if (msg == WM_MSIME_RECONVERTREQUEST) return "WM_MSIME_RECONVERTREQUEST"; + else if (msg == WM_MSIME_RECONVERT) return "WM_MSIME_RECONVERT"; + else if (msg == WM_MSIME_QUERYPOSITION) return "WM_MSIME_QUERYPOSITION"; + else if (msg == WM_MSIME_DOCUMENTFEED) return "WM_MSIME_DOCUMENTFEED"; + return wine_dbg_sprintf( "%#x", msg ); + } +} From bd13ecdde90bbe69fa151ce35a5404644793ae4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 11:54:57 +0200 Subject: [PATCH 232/758] imm32: Call DefWindowProcW from IME UI for unhandled messages. (cherry picked from commit 617e24233b0bc4775fea58c9fe1c1672349de224) --- dlls/imm32/ime.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 42f19b7cb2b..a77dfbe09e1 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -246,7 +246,6 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); INPUTCONTEXT *ctx; - LRESULT ret = 0; TRACE( "hwnd %p, himc %p, msg %s, wparam %#Ix, lparam %#Ix\n", hwnd, himc, debugstr_wm_ime(msg), wparam, lparam ); @@ -288,8 +287,6 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP case WM_PAINT: ime_ui_paint( himc, hwnd ); return FALSE; - case WM_NCCREATE: - return TRUE; case WM_SETFOCUS: if (wparam) SetFocus( (HWND)wparam ); else FIXME( "Received focus, should never have focus\n" ); @@ -313,12 +310,7 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP return 1; } - /* DefWndProc if not an IME message */ - if (!ret && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || - (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) - ret = DefWindowProcW( hwnd, msg, wparam, lparam ); - - return ret; + return DefWindowProcW( hwnd, msg, wparam, lparam ); } static WNDCLASSEXW ime_ui_class = From f39117f09062c6be8a243f33dcef6023faf1c620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 21:37:23 +0200 Subject: [PATCH 233/758] imm32: Remove unnecessary HIMC check in IME UI window proc. (cherry picked from commit e04079160d6519bf06d8213350e26fd46dd2051d) --- dlls/imm32/ime.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index a77dfbe09e1..3c4550c3cd9 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -250,23 +250,6 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP TRACE( "hwnd %p, himc %p, msg %s, wparam %#Ix, lparam %#Ix\n", hwnd, himc, debugstr_wm_ime(msg), wparam, lparam ); - /* if we have no himc there are many messages we cannot process */ - if (!himc) - { - switch (msg) - { - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_IME_NOTIFY: - case WM_IME_CONTROL: - case WM_IME_COMPOSITIONFULL: - case WM_IME_SELECT: - case WM_IME_CHAR: return 0L; - default: break; - } - } - switch (msg) { case WM_CREATE: From be98086b57540c6e779a343f917a611fd9eb9df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 8 Apr 2023 08:53:26 +0200 Subject: [PATCH 234/758] imm32/tests: Reduce the number of IME installations. (cherry picked from commit 152d6e8b2760fc316b1fa06e97b225c536275a63) --- dlls/imm32/tests/imm32.c | 184 +++++++++++++++++---------------------- 1 file changed, 81 insertions(+), 103 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 82809f37f8e..f91902eb833 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2574,7 +2574,7 @@ static IMEINFO ime_info; static UINT ime_count; static WCHAR ime_path[MAX_PATH]; static HIMC default_himc; -static HKL default_hkl; +static HKL default_hkl, wineime_hkl; static HKL expect_ime = (HKL)(int)0xe020047f; enum ime_function @@ -3421,12 +3421,8 @@ static void test_ImmInstallIME(void) ret = ImmFreeLayout( hkl ); ok( ret, "ImmFreeLayout returned %#x\n", ret ); - ime_cleanup( hkl, FALSE ); - ime_info.fdwProperty = 0; - if (!(hkl = ime_install())) goto cleanup; - SET_EXPECT( IME_DLL_PROCESS_ATTACH ); SET_EXPECT( ImeInquire ); ret = ImmLoadIME( hkl ); @@ -3473,7 +3469,7 @@ static void test_ImmIsIME(void) /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ ime_info.fdwProperty = IME_PROP_END_UNLOAD; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; todo_ImeInquire = TRUE; todo_ImeDestroy = TRUE; @@ -3485,8 +3481,6 @@ static void test_ImmIsIME(void) todo_ImeInquire = FALSE; todo_ImeDestroy = FALSE; - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); SET_ENABLE( ImeInquire, FALSE ); @@ -3558,7 +3552,7 @@ static void test_ImmGetProperty(void) /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ ime_info.fdwProperty = IME_PROP_END_UNLOAD; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; SET_EXPECT( ImeInquire ); SET_EXPECT( ImeDestroy ); @@ -3581,8 +3575,6 @@ static void test_ImmGetProperty(void) todo_ImeDestroy = FALSE; called_ImeDestroy = FALSE; - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( ImeInquire, FALSE ); SET_ENABLE( ImeDestroy, FALSE ); @@ -3616,7 +3608,7 @@ static void test_ImmGetDescription(void) ret = GetLastError(); ok( ret == 0xdeadbeef, "got error %lu\n", ret ); - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetDescriptionW( hkl, bufferW, 2 ); @@ -3654,8 +3646,6 @@ static void test_ImmGetDescription(void) ok( ret == 12, "ImmGetDescriptionA returned %lu\n", ret ); ok( !strcmp( bufferA, "WineTest IME" ), "got bufferA %s\n", debugstr_a(bufferA) ); - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); SET_ENABLE( ImeInquire, FALSE ); @@ -3691,7 +3681,7 @@ static void test_ImmGetIMEFileName(void) ret = GetLastError(); ok( ret == 0xdeadbeef, "got error %lu\n", ret ); - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; memset( bufferW, 0xcd, sizeof(bufferW) ); ret = ImmGetIMEFileNameW( hkl, bufferW, 2 ); @@ -3733,8 +3723,6 @@ static void test_ImmGetIMEFileName(void) ok( ret == 12, "ImmGetIMEFileNameA returned %lu\n", ret ); ok( !strcmp( bufferA, expectA ), "got bufferA %s\n", debugstr_a(bufferA) ); - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( IME_DLL_PROCESS_ATTACH, FALSE ); SET_ENABLE( ImeInquire, FALSE ); @@ -3770,7 +3758,7 @@ static void test_ImmEscape( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; for (i = 0; i < ARRAY_SIZE(codes); ++i) { @@ -3841,8 +3829,6 @@ static void test_ImmEscape( BOOL unicode ) winetest_pop_context(); } - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( ImeEscape, FALSE ); @@ -3891,7 +3877,7 @@ static void test_ImmEnumRegisterWord( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; SET_EXPECT( ImeEnumRegisterWord ); ok_ret( 0, ImmEnumRegisterWordW( hkl, enum_register_wordW, NULL, 0, NULL, NULL ) ); @@ -3909,8 +3895,6 @@ static void test_ImmEnumRegisterWord( BOOL unicode ) ok_ret( 0xdeadbeef, ImmEnumRegisterWordA( hkl, enum_register_wordA, "Reading", 0xdeadbeef, "String", NULL ) ); CHECK_CALLED( ImeEnumRegisterWord ); - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( ImeEnumRegisterWord, FALSE ); @@ -3937,7 +3921,7 @@ static void test_ImmRegisterWord( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; SET_EXPECT( ImeRegisterWord ); ok_ret( 0, ImmRegisterWordW( hkl, NULL, 0, NULL ) ); @@ -3971,8 +3955,6 @@ static void test_ImmRegisterWord( BOOL unicode ) ok_ret( 0, ImmRegisterWordA( hkl, NULL, 0, "String" ) ); CHECK_CALLED( ImeRegisterWord ); - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( ImeRegisterWord, FALSE ); @@ -4001,7 +3983,7 @@ static void test_ImmGetRegisterWordStyle( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; if (!strcmp( winetest_platform, "wine" )) goto skip_null; @@ -4048,8 +4030,6 @@ static void test_ImmGetRegisterWordStyle( BOOL unicode ) } CHECK_CALLED( ImeGetRegisterWordStyle ); - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( ImeGetRegisterWordStyle, FALSE ); @@ -4076,7 +4056,7 @@ static void test_ImmUnregisterWord( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; SET_EXPECT( ImeUnregisterWord ); ok_ret( 0, ImmUnregisterWordW( hkl, NULL, 0, NULL ) ); @@ -4110,8 +4090,6 @@ static void test_ImmUnregisterWord( BOOL unicode ) ok_ret( 0, ImmUnregisterWordA( hkl, NULL, 0, "String" ) ); CHECK_CALLED( ImeUnregisterWord ); - ime_cleanup( hkl, FALSE ); - cleanup: SET_ENABLE( ImeUnregisterWord, FALSE ); @@ -4186,7 +4164,7 @@ static void test_ImmSetConversionStatus(void) {0}, }; DWORD old_conversion, old_sentence, conversion, sentence; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; INPUTCONTEXT *ctx; ok_ret( 0, ImmGetConversionStatus( 0, &old_conversion, &old_sentence ) ); @@ -4217,7 +4195,7 @@ static void test_ImmSetConversionStatus(void) ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; ok_ret( 1, ImmActivateLayout( hkl ) ); ok_ret( 1, ImmLoadIME( hkl ) ); @@ -4282,17 +4260,17 @@ static void test_ImmSetConversionStatus(void) ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); /* status is cached and some bits are kept from the previous active IME */ - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" ); ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); ok_ret( 1, ImmActivateLayout( hkl ) ); todo_wine ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" ); todo_wine ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" ); ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); cleanup: /* sanitize conversion status to some sane default */ @@ -4357,7 +4335,7 @@ static void test_ImmSetOpenStatus(void) }, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; DWORD old_status, status; INPUTCONTEXT *ctx; @@ -4384,7 +4362,7 @@ static void test_ImmSetOpenStatus(void) ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; ok_ret( 1, ImmActivateLayout( hkl ) ); ok_ret( 1, ImmLoadIME( hkl ) ); @@ -4439,7 +4417,7 @@ static void test_ImmSetOpenStatus(void) /* status is cached between IME activations */ - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); status = ImmGetOpenStatus( default_himc ); todo_wine ok_eq( old_status, status, UINT, "%#x" ); todo_wine ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); @@ -4448,12 +4426,12 @@ static void test_ImmSetOpenStatus(void) todo_wine ok_eq( 1, status, UINT, "%#x" ); todo_wine ok_eq( 1, ctx->fOpen, UINT, "%#x" ); ok_ret( 1, ImmSetOpenStatus( default_himc, 0 ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); status = ImmGetOpenStatus( default_himc ); ok_eq( old_status, status, UINT, "%#x" ); ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); cleanup: /* sanitize open status to some sane default */ @@ -4480,7 +4458,7 @@ static void test_ImmProcessKey(void) }, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; UINT_PTR ret; hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, @@ -4488,12 +4466,12 @@ static void test_ImmProcessKey(void) ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); process_messages(); - ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) ); + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', 0, 0 ) ); ok_seq( empty_sequence ); ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; ok_ret( 1, ImmActivateLayout( hkl ) ); ok_ret( 1, ImmLoadIME( hkl ) ); @@ -4510,13 +4488,13 @@ static void test_ImmProcessKey(void) ok_seq( process_key_seq ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ok_ret( 0, ImmProcessKey( hwnd, old_hkl, 'A', 0, 0 ) ); + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', 0, 0 ) ); ok_seq( empty_sequence ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); process_messages(); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -4650,7 +4628,7 @@ static void test_ImmActivateLayout(void) }, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; struct ime_windows ime_windows = {0}; HIMC himc; UINT ret; @@ -4658,20 +4636,20 @@ static void test_ImmActivateLayout(void) SET_ENABLE( ImeInquire, TRUE ); SET_ENABLE( ImeDestroy, TRUE ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; /* ActivateKeyboardLayout doesn't call ImeInquire / ImeDestroy */ ok_seq( empty_sequence ); - ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" ); ok_seq( empty_sequence ); - ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); /* ImmActivateLayout changes active HKL */ @@ -4688,14 +4666,11 @@ static void test_ImmActivateLayout(void) ok_seq( empty_sequence ); todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_seq( deactivate_seq ); todo_ImeDestroy = FALSE; - ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - - ime_cleanup( hkl, FALSE ); - ok_seq( empty_sequence ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); /* ImmActivateLayout leaks the IME, we need to free it manually */ @@ -4709,8 +4684,6 @@ static void test_ImmActivateLayout(void) /* when there's a window, ActivateKeyboardLayout calls ImeInquire */ - if (!(hkl = ime_install())) goto cleanup; - hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); @@ -4722,7 +4695,7 @@ static void test_ImmActivateLayout(void) ok_seq( empty_sequence ); SET_EXPECT( ImeInquire ); - ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); CHECK_CALLED( ImeInquire ); activate_with_window_seq[1].himc = himc; ok_seq( activate_with_window_seq ); @@ -4738,20 +4711,20 @@ static void test_ImmActivateLayout(void) ime_call_count = 0; todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ - ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" ); todo_ImeDestroy = FALSE; deactivate_with_window_seq[1].himc = himc; deactivate_with_window_seq[5].himc = himc; ok_seq( deactivate_with_window_seq ); - ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); ok_ret( 1, ImmDestroyContext( himc ) ); ok_seq( empty_sequence ); todo_ImeInquire = TRUE; - ok_eq( old_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); + ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); todo_ImeInquire = FALSE; ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -4761,13 +4734,14 @@ static void test_ImmActivateLayout(void) ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ - ok_eq( hkl, ActivateKeyboardLayout( old_hkl, 0 ), HKL, "%p" ); + ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" ); todo_ImeDestroy = FALSE; - ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + process_messages(); SET_EXPECT( ImeDestroy ); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); CHECK_CALLED( ImeDestroy ); ok_ret( 1, DestroyWindow( hwnd ) ); @@ -4875,7 +4849,7 @@ static void test_ImmCreateInputContext(void) }, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; INPUTCONTEXT *ctx; HIMC himc[2]; HWND hwnd; @@ -4912,7 +4886,7 @@ static void test_ImmCreateInputContext(void) ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; ime_info.dwPrivateDataSize = 123; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; ok_ret( 1, ImmLoadIME( hkl ) ); @@ -4956,14 +4930,14 @@ static void test_ImmCreateInputContext(void) /* Deactivating the layout calls ImeSelect 0 on existing HIMC */ - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); deactivate_seq[1].himc = himc[0]; deactivate_seq[5].himc = himc[0]; ok_seq( deactivate_seq ); - ok_eq( old_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); ok_seq( empty_sequence ); cleanup: @@ -5003,12 +4977,12 @@ static void test_DefWindowProc(void) {.hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY}}, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; UINT_PTR ret; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) return; + if (!(hkl = wineime_hkl)) return; hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5051,11 +5025,11 @@ static void test_DefWindowProc(void) ok_ret( 0, ret ); ok_seq( empty_sequence ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; } @@ -5129,12 +5103,12 @@ static void test_ImmSetActiveContext(void) }, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; HIMC himc; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) return; + if (!(hkl = wineime_hkl)) return; hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5166,11 +5140,11 @@ static void test_ImmSetActiveContext(void) ok_seq( activate_1_seq ); ok_ret( 1, ImmDestroyContext( himc ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; } @@ -5233,7 +5207,7 @@ static void test_ImmRequestMessage(void) }, {0}, }; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; COMPOSITIONFORM comp_form = {0}; IMECHARPOSITION char_pos = {0}; RECONVERTSTRING reconv = {0}; @@ -5242,7 +5216,7 @@ static void test_ImmRequestMessage(void) INPUTCONTEXT *ctx; HIMC himc; - if (!(hkl = ime_install())) return; + if (!(hkl = wineime_hkl)) return; hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5301,11 +5275,11 @@ static void test_ImmRequestMessage(void) ok_ret( 1, ImmUnlockIMC( himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; } @@ -5314,7 +5288,7 @@ static void test_ImmGetCandidateList( BOOL unicode ) { char buffer[512], expect_bufW[512] = {0}, expect_bufA[512] = {0}; CANDIDATELIST *cand_list = (CANDIDATELIST *)buffer, *expect_listW, *expect_listA; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; CANDIDATEINFO *cand_info; INPUTCONTEXT *ctx; HIMC himc; @@ -5349,7 +5323,7 @@ static void test_ImmGetCandidateList( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5426,11 +5400,11 @@ static void test_ImmGetCandidateList( BOOL unicode ) ok_ret( 1, ImmUnlockIMC( himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -5440,7 +5414,7 @@ static void test_ImmGetCandidateList( BOOL unicode ) static void test_ImmGetCandidateListCount( BOOL unicode ) { - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; CANDIDATEINFO *cand_info; INPUTCONTEXT *ctx; DWORD count; @@ -5452,7 +5426,7 @@ static void test_ImmGetCandidateListCount( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5499,11 +5473,11 @@ static void test_ImmGetCandidateListCount( BOOL unicode ) ok_ret( 1, ImmUnlockIMC( himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -5513,7 +5487,7 @@ static void test_ImmGetCandidateListCount( BOOL unicode ) static void test_ImmGetCandidateWindow(void) { - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; const CANDIDATEFORM expect_form = { .dwIndex = 0xdeadbeef, @@ -5527,7 +5501,7 @@ static void test_ImmGetCandidateWindow(void) ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; - if (!(hkl = ime_install())) return; + if (!(hkl = wineime_hkl)) return; hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5576,11 +5550,11 @@ static void test_ImmGetCandidateWindow(void) ok_ret( 1, ImmUnlockIMC( himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; } @@ -5667,7 +5641,7 @@ static void test_ImmGetCompositionString( BOOL unicode ) }; static const UINT expect_retW[ARRAY_SIZE(gcs_indexes)] = {16, 8, 8, 8, 4, 8, 3, 1, 20, 8, 12, 8}; static const UINT expect_retA[ARRAY_SIZE(gcs_indexes)] = {8, 8, 8, 4, 4, 8, 3, 1, 10, 8, 6, 8}; - HKL hkl, old_hkl = GetKeyboardLayout( 0 ); + HKL hkl; COMPOSITIONSTRING *string; char buffer[1024]; INPUTCONTEXT *old_ctx, *ctx; @@ -5685,7 +5659,7 @@ static void test_ImmGetCompositionString( BOOL unicode ) ime_info.fdwProperty = IME_PROP_END_UNLOAD; if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; - if (!(hkl = ime_install())) goto cleanup; + if (!(hkl = wineime_hkl)) goto cleanup; hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5930,7 +5904,7 @@ static void test_ImmGetCompositionString( BOOL unicode ) ok_ret( 1, ImmUnlockIMC( himc ) ); /* composition strings are kept between IME selections */ - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ctx = ImmLockIMC( himc ); ok_eq( old_ctx, ctx, INPUTCONTEXT *, "%p" ); ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" ); @@ -5941,18 +5915,18 @@ static void test_ImmGetCompositionString( BOOL unicode ) ok_ret( 1, ImmActivateLayout( hkl ) ); ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" ); check_composition_string( string, &expect_string_empty ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_eq( old_himcc, ctx->hCompStr, HIMCC, "%p" ); check_composition_string( string, &expect_string_empty ); ok_ret( 1, ImmUnlockIMC( himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); - ok_ret( 1, ImmActivateLayout( old_hkl ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, DestroyWindow( hwnd ) ); process_messages(); - ime_cleanup( hkl, TRUE ); + ok_ret( 1, ImmFreeLayout( hkl ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -5979,6 +5953,8 @@ START_TEST(imm32) test_ImmEnumInputContext(); test_ImmInstallIME(); + wineime_hkl = ime_install(); + test_ImmGetDescription(); test_ImmGetIMEFileName(); test_ImmIsIME(); @@ -6016,6 +5992,8 @@ START_TEST(imm32) test_ImmGetCompositionString( TRUE ); test_ImmGetCompositionString( FALSE ); + if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); + if (init()) { test_ImmNotifyIME(); From 3e051b2ef2c40a0f86b51f39891808ba6b156a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 8 Apr 2023 09:36:41 +0200 Subject: [PATCH 235/758] imm32/tests: Cleanup the cross thread IMC tests. (cherry picked from commit ff08b083fd25b29cd2d8c965bfec58efebcaa1c7) --- dlls/imm32/tests/imm32.c | 535 +++++++++++++++++++++++---------------- 1 file changed, 311 insertions(+), 224 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index f91902eb833..827a1326e3e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -986,248 +986,335 @@ static void test_NtUserAssociateInputContext(void) ImmReleaseContext(hwnd,imc); } -typedef struct _igc_threadinfo { +struct test_cross_thread_himc_params +{ HWND hwnd; HANDLE event; - HIMC himc; - HIMC u_himc; -} igc_threadinfo; - + HIMC himc[2]; + INPUTCONTEXT *contexts[2]; +}; -static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam) +static DWORD WINAPI test_cross_thread_himc_thread( void *arg ) { - HIMC h1,h2; - HWND hwnd2; - COMPOSITIONFORM cf; - CANDIDATEFORM cdf; - POINT pt; + CANDIDATEFORM candidate = {.dwIndex = 1, .dwStyle = CFS_CANDIDATEPOS}; + struct test_cross_thread_himc_params *params = arg; + COMPOSITIONFORM composition = {0}; + INPUTCONTEXT *contexts[2]; + HIMC himc[2], tmp_himc; + HWND hwnd, tmp_hwnd; + POINT pos = {0}; MSG msg; - igc_threadinfo *info= (igc_threadinfo*)lpParam; - info->hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test", - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL); - - h1 = ImmGetContext(hwnd); - ok(info->himc == h1, "hwnd context changed in new thread\n"); - h2 = ImmGetContext(info->hwnd); - ok(h2 != h1, "new hwnd in new thread should have different context\n"); - info->himc = h2; - ImmReleaseContext(hwnd,h1); - - hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test", - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL); - h1 = ImmGetContext(hwnd2); + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok_ne( NULL, hwnd, HWND, "%p" ); + himc[0] = ImmGetContext( hwnd ); + ok_ne( NULL, himc[0], HIMC, "%p" ); + contexts[0] = ImmLockIMC( himc[0] ); + ok_ne( NULL, contexts[0], INPUTCONTEXT *, "%p" ); + + tmp_hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + tmp_himc = ImmGetContext( tmp_hwnd ); + ok_eq( himc[0], tmp_himc, HIMC, "%p" ); + ok_ret( 1, ImmReleaseContext( tmp_hwnd, tmp_himc ) ); + ok_ret( 1, DestroyWindow( tmp_hwnd ) ); - ok(h1 == h2, "Windows in same thread should have same default context\n"); - ImmReleaseContext(hwnd2,h1); - ImmReleaseContext(info->hwnd,h2); - DestroyWindow(hwnd2); + himc[1] = ImmCreateContext(); + ok_ne( NULL, himc[1], HIMC, "%p" ); + contexts[1] = ImmLockIMC( himc[1] ); + ok_ne( NULL, contexts[1], INPUTCONTEXT *, "%p" ); + + ok_ret( 1, ImmSetOpenStatus( himc[0], 0xdeadbeef ) ); + ok_ret( 1, ImmSetOpenStatus( himc[1], 0xfeedcafe ) ); + ok_ret( 1, ImmSetCompositionWindow( himc[0], &composition ) ); + ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) ); + ok_ret( 1, ImmSetCandidateWindow( himc[0], &candidate ) ); + ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) ); + ok_ret( 1, ImmSetStatusWindowPos( himc[0], &pos ) ); + ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) ); + + params->hwnd = hwnd; + params->himc[0] = himc[0]; + params->himc[1] = himc[1]; + params->contexts[0] = contexts[0]; + params->contexts[1] = contexts[1]; + SetEvent( params->event ); + + while (GetMessageW( &msg, 0, 0, 0 )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } - /* priming for later tests */ - ImmSetCompositionWindow(h1, &cf); - ImmSetStatusWindowPos(h1, &pt); - info->u_himc = ImmCreateContext(); - ImmSetOpenStatus(info->u_himc, TRUE); - cdf.dwIndex = 0; - cdf.dwStyle = CFS_CANDIDATEPOS; - cdf.ptCurrentPos.x = 0; - cdf.ptCurrentPos.y = 0; - ImmSetCandidateWindow(info->u_himc, &cdf); + ok_ret( 1, ImmUnlockIMC( himc[0] ) ); + ok_ret( 1, ImmUnlockIMC( himc[1] ) ); - SetEvent(info->event); + ok_ret( 1, ImmDestroyContext( himc[1] ) ); + ok_ret( 1, ImmReleaseContext( hwnd, himc[0] ) ); + ok_ret( 0, DestroyWindow( hwnd ) ); - while(GetMessageW(&msg, 0, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } return 1; } -static void test_ImmThreads(void) +static void test_cross_thread_himc(void) { - HIMC himc, otherHimc, h1; - igc_threadinfo threadinfo; - HANDLE hThread; - DWORD dwThreadId; - BOOL rc; - LOGFONTA lf; - COMPOSITIONFORM cf; - CANDIDATEFORM cdf; - DWORD status, sentence; - POINT pt; + static const WCHAR comp_string[] = L"CompString"; + struct test_cross_thread_himc_params params; + COMPOSITIONFORM composition = {0}; + DWORD tid, conversion, sentence; + CANDIDATEFORM candidate = {0}; + COMPOSITIONSTRING *string; + HIMC himc[2], tmp_himc; + INPUTCONTEXT *tmp_ctx; + LOGFONTW fontW = {0}; + LOGFONTA fontA = {0}; + char buffer[512]; + POINT pos = {0}; + HANDLE thread; + BYTE *dst; + UINT ret; - himc = ImmGetContext(hwnd); - threadinfo.event = CreateEventA(NULL, TRUE, FALSE, NULL); - threadinfo.himc = himc; - hThread = CreateThread(NULL, 0, ImmGetContextThreadFunc, &threadinfo, 0, &dwThreadId ); - WaitForSingleObject(threadinfo.event, INFINITE); + himc[0] = ImmGetContext( hwnd ); + ok_ne( NULL, himc[0], HIMC, "%p" ); + ok_ne( NULL, ImmLockIMC( himc[0] ), INPUTCONTEXT *, "%p" ); + ok_ret( 1, ImmUnlockIMC( himc[0] ) ); - otherHimc = ImmGetContext(threadinfo.hwnd); + params.event = CreateEventW(NULL, TRUE, FALSE, NULL); + ok_ne( NULL, params.event, HANDLE, "%p" ); + thread = CreateThread( NULL, 0, test_cross_thread_himc_thread, ¶ms, 0, &tid ); + ok_ne( NULL, thread, HANDLE, "%p" ); + WaitForSingleObject( params.event, INFINITE ); - ok(himc != otherHimc, "Windows from other threads should have different himc\n"); - ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n"); + tmp_himc = ImmGetContext( params.hwnd ); + ok_ne( himc[0], tmp_himc, HIMC, "%p" ); + ok_eq( params.himc[0], tmp_himc, HIMC, "%p" ); + ok_ret( 1, ImmReleaseContext( params.hwnd, tmp_himc ) ); - SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, TRUE); - SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, TRUE); - SET_EXPECT(WM_IME_SETCONTEXT_ACTIVATE); - rc = ImmSetActiveContext(hwnd, otherHimc, TRUE); - ok(rc, "ImmSetActiveContext failed\n"); - CHECK_CALLED(WM_IME_SETCONTEXT_ACTIVATE); - SET_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE); - rc = ImmSetActiveContext(hwnd, otherHimc, FALSE); - ok(rc, "ImmSetActiveContext failed\n"); - CHECK_CALLED(WM_IME_SETCONTEXT_DEACTIVATE); - SET_ENABLE(WM_IME_SETCONTEXT_DEACTIVATE, FALSE); - SET_ENABLE(WM_IME_SETCONTEXT_ACTIVATE, FALSE); + himc[1] = ImmCreateContext(); + ok_ne( NULL, himc[1], HIMC, "%p" ); + tmp_ctx = ImmLockIMC( himc[1] ); + ok_ne( NULL, tmp_ctx, INPUTCONTEXT *, "%p" ); + + tmp_ctx->hCompStr = ImmReSizeIMCC( tmp_ctx->hCompStr, 512 ); + ok_ne( NULL, tmp_ctx->hCompStr, HIMCC, "%p" ); + string = ImmLockIMCC( tmp_ctx->hCompStr ); + ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" ); + string->dwSize = sizeof(COMPOSITIONSTRING); + string->dwCompStrLen = wcslen( comp_string ); + string->dwCompStrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompStrOffset; + memcpy( dst, comp_string, string->dwCompStrLen * sizeof(WCHAR) ); + string->dwSize += string->dwCompStrLen * sizeof(WCHAR); - h1 = ImmAssociateContext(hwnd,otherHimc); - ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n"); - h1 = ImmGetContext(hwnd); - ok(h1 == himc, "Context for window should remain unchanged\n"); - ImmReleaseContext(hwnd,h1); - - h1 = ImmAssociateContext(hwnd, threadinfo.u_himc); - ok (h1 == NULL, "Should fail to associate a context from a different thread\n"); - h1 = ImmGetContext(hwnd); - ok(h1 == himc, "Context for window should remain unchanged\n"); - ImmReleaseContext(hwnd,h1); - - h1 = ImmAssociateContext(threadinfo.hwnd, threadinfo.u_himc); - ok (h1 == NULL, "Should fail to associate a context from a different thread into a window from that thread.\n"); - h1 = ImmGetContext(threadinfo.hwnd); - ok(h1 == threadinfo.himc, "Context for window should remain unchanged\n"); - ImmReleaseContext(threadinfo.hwnd,h1); - - /* OpenStatus */ - rc = ImmSetOpenStatus(himc, TRUE); - ok(rc != 0, "ImmSetOpenStatus failed\n"); - rc = ImmGetOpenStatus(himc); - ok(rc != 0, "ImmGetOpenStatus failed\n"); - rc = ImmSetOpenStatus(himc, FALSE); - ok(rc != 0, "ImmSetOpenStatus failed\n"); - rc = ImmGetOpenStatus(himc); - ok(rc == 0, "ImmGetOpenStatus failed\n"); - - rc = ImmSetOpenStatus(otherHimc, TRUE); - ok(rc == 0, "ImmSetOpenStatus should fail\n"); - rc = ImmSetOpenStatus(threadinfo.u_himc, TRUE); - ok(rc == 0, "ImmSetOpenStatus should fail\n"); - rc = ImmGetOpenStatus(otherHimc); - ok(rc == 0, "ImmGetOpenStatus failed\n"); - rc = ImmGetOpenStatus(threadinfo.u_himc); - ok (rc == 1 || broken(rc == 0), "ImmGetOpenStatus should return 1\n"); - rc = ImmSetOpenStatus(otherHimc, FALSE); - ok(rc == 0, "ImmSetOpenStatus should fail\n"); - rc = ImmGetOpenStatus(otherHimc); - ok(rc == 0, "ImmGetOpenStatus failed\n"); - - /* CompositionFont */ - rc = ImmGetCompositionFontA(himc, &lf); - ok(rc != 0, "ImmGetCompositionFont failed\n"); - rc = ImmSetCompositionFontA(himc, &lf); - ok(rc != 0, "ImmSetCompositionFont failed\n"); - - rc = ImmGetCompositionFontA(otherHimc, &lf); - ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n"); - rc = ImmGetCompositionFontA(threadinfo.u_himc, &lf); - ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont user himc failed\n"); - rc = ImmSetCompositionFontA(otherHimc, &lf); - ok(rc == 0, "ImmSetCompositionFont should fail\n"); - rc = ImmSetCompositionFontA(threadinfo.u_himc, &lf); - ok(rc == 0, "ImmSetCompositionFont should fail\n"); - - /* CompositionWindow */ - rc = ImmSetCompositionWindow(himc, &cf); - ok(rc != 0, "ImmSetCompositionWindow failed\n"); - rc = ImmGetCompositionWindow(himc, &cf); - ok(rc != 0, "ImmGetCompositionWindow failed\n"); - - rc = ImmSetCompositionWindow(otherHimc, &cf); - ok(rc == 0, "ImmSetCompositionWindow should fail\n"); - rc = ImmSetCompositionWindow(threadinfo.u_himc, &cf); - ok(rc == 0, "ImmSetCompositionWindow should fail\n"); - rc = ImmGetCompositionWindow(otherHimc, &cf); - ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n"); - rc = ImmGetCompositionWindow(threadinfo.u_himc, &cf); - ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n"); - - /* ConversionStatus */ - rc = ImmGetConversionStatus(himc, &status, &sentence); - ok(rc != 0, "ImmGetConversionStatus failed\n"); - rc = ImmSetConversionStatus(himc, status, sentence); - ok(rc != 0, "ImmSetConversionStatus failed\n"); - - rc = ImmGetConversionStatus(otherHimc, &status, &sentence); - ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n"); - rc = ImmGetConversionStatus(threadinfo.u_himc, &status, &sentence); - ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n"); - rc = ImmSetConversionStatus(otherHimc, status, sentence); - ok(rc == 0, "ImmSetConversionStatus should fail\n"); - rc = ImmSetConversionStatus(threadinfo.u_himc, status, sentence); - ok(rc == 0, "ImmSetConversionStatus should fail\n"); - - /* StatusWindowPos */ - rc = ImmSetStatusWindowPos(himc, &pt); - ok(rc != 0, "ImmSetStatusWindowPos failed\n"); - rc = ImmGetStatusWindowPos(himc, &pt); - ok(rc != 0, "ImmGetStatusWindowPos failed\n"); - - rc = ImmSetStatusWindowPos(otherHimc, &pt); - ok(rc == 0, "ImmSetStatusWindowPos should fail\n"); - rc = ImmSetStatusWindowPos(threadinfo.u_himc, &pt); - ok(rc == 0, "ImmSetStatusWindowPos should fail\n"); - rc = ImmGetStatusWindowPos(otherHimc, &pt); - ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n"); - rc = ImmGetStatusWindowPos(threadinfo.u_himc, &pt); - ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n"); - - h1 = ImmAssociateContext(threadinfo.hwnd, NULL); - ok (h1 == otherHimc, "ImmAssociateContext cross thread with NULL should work\n"); - h1 = ImmGetContext(threadinfo.hwnd); - ok (h1 == NULL, "CrossThread window context should be NULL\n"); - h1 = ImmAssociateContext(threadinfo.hwnd, h1); - ok (h1 == NULL, "Resetting cross thread context should fail\n"); - h1 = ImmGetContext(threadinfo.hwnd); - ok (h1 == NULL, "CrossThread window context should still be NULL\n"); - - rc = ImmDestroyContext(threadinfo.u_himc); - ok (rc == 0, "ImmDestroyContext Cross Thread should fail\n"); - - /* Candidate Window */ - rc = ImmGetCandidateWindow(himc, 0, &cdf); - ok (rc == 0, "ImmGetCandidateWindow should fail\n"); - cdf.dwIndex = 0; - cdf.dwStyle = CFS_CANDIDATEPOS; - cdf.ptCurrentPos.x = 0; - cdf.ptCurrentPos.y = 0; - rc = ImmSetCandidateWindow(himc, &cdf); - ok (rc == 1, "ImmSetCandidateWindow should succeed\n"); - rc = ImmGetCandidateWindow(himc, 0, &cdf); - ok (rc == 1, "ImmGetCandidateWindow should succeed\n"); - - rc = ImmGetCandidateWindow(otherHimc, 0, &cdf); - ok (rc == 0, "ImmGetCandidateWindow should fail\n"); - rc = ImmSetCandidateWindow(otherHimc, &cdf); - ok (rc == 0, "ImmSetCandidateWindow should fail\n"); - rc = ImmGetCandidateWindow(threadinfo.u_himc, 0, &cdf); - ok (rc == 1 || broken( rc == 0), "ImmGetCandidateWindow should succeed\n"); - rc = ImmSetCandidateWindow(threadinfo.u_himc, &cdf); - ok (rc == 0, "ImmSetCandidateWindow should fail\n"); - - ImmReleaseContext(threadinfo.hwnd,otherHimc); - ImmReleaseContext(hwnd,himc); - - SendMessageA(threadinfo.hwnd, WM_CLOSE, 0, 0); - rc = PostThreadMessageA(dwThreadId, WM_QUIT, 1, 0); - ok(rc == 1, "PostThreadMessage should succeed\n"); - WaitForSingleObject(hThread, INFINITE); - CloseHandle(hThread); - - himc = ImmGetContext(GetDesktopWindow()); - ok(himc == NULL, "Should not be able to get himc from other process window\n"); + string->dwCompClauseLen = 2 * sizeof(DWORD); + string->dwCompClauseOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompClauseOffset; + *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0; + *(DWORD *)(dst + 1 * sizeof(DWORD)) = string->dwCompStrLen; + string->dwSize += 2 * sizeof(DWORD); + + string->dwCompAttrLen = string->dwCompStrLen; + string->dwCompAttrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompAttrOffset; + memset( dst, ATTR_INPUT, string->dwCompStrLen ); + string->dwSize += string->dwCompStrLen; + ok_ret( 0, ImmUnlockIMCC( tmp_ctx->hCompStr ) ); + + ok_ret( 1, ImmUnlockIMC( himc[1] ) ); + + /* ImmLockIMC should succeed with cross thread HIMC */ + + tmp_ctx = ImmLockIMC( params.himc[0] ); + ok_eq( params.contexts[0], tmp_ctx, INPUTCONTEXT *, "%p" ); + ret = ImmGetIMCLockCount( params.himc[0] ); + ok( ret >= 2, "got ret %u\n", ret ); + + tmp_ctx->hCompStr = ImmReSizeIMCC( tmp_ctx->hCompStr, 512 ); + ok_ne( NULL, tmp_ctx->hCompStr, HIMCC, "%p" ); + string = ImmLockIMCC( tmp_ctx->hCompStr ); + ok_ne( NULL, string, COMPOSITIONSTRING *, "%p" ); + string->dwSize = sizeof(COMPOSITIONSTRING); + string->dwCompStrLen = wcslen( comp_string ); + string->dwCompStrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompStrOffset; + memcpy( dst, comp_string, string->dwCompStrLen * sizeof(WCHAR) ); + string->dwSize += string->dwCompStrLen * sizeof(WCHAR); + + string->dwCompClauseLen = 2 * sizeof(DWORD); + string->dwCompClauseOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompClauseOffset; + *(DWORD *)(dst + 0 * sizeof(DWORD)) = 0; + *(DWORD *)(dst + 1 * sizeof(DWORD)) = string->dwCompStrLen; + string->dwSize += 2 * sizeof(DWORD); + + string->dwCompAttrLen = string->dwCompStrLen; + string->dwCompAttrOffset = string->dwSize; + dst = (BYTE *)string + string->dwCompAttrOffset; + memset( dst, ATTR_INPUT, string->dwCompStrLen ); + string->dwSize += string->dwCompStrLen; + ok_ret( 0, ImmUnlockIMCC( tmp_ctx->hCompStr ) ); + + ok_ret( 1, ImmUnlockIMC( params.himc[0] ) ); + + tmp_ctx = ImmLockIMC( params.himc[1] ); + ok_eq( params.contexts[1], tmp_ctx, INPUTCONTEXT *, "%p" ); + ret = ImmGetIMCLockCount( params.himc[1] ); + ok( ret >= 2, "got ret %u\n", ret ); + ok_ret( 1, ImmUnlockIMC( params.himc[1] ) ); + + /* ImmSetActiveContext should succeed with cross thread HIMC */ + + SET_ENABLE( WM_IME_SETCONTEXT_DEACTIVATE, TRUE ); + SET_ENABLE( WM_IME_SETCONTEXT_ACTIVATE, TRUE ); + + SET_EXPECT( WM_IME_SETCONTEXT_ACTIVATE ); + ok_ret( 1, ImmSetActiveContext( hwnd, params.himc[0], TRUE ) ); + CHECK_CALLED( WM_IME_SETCONTEXT_ACTIVATE ); + + SET_EXPECT( WM_IME_SETCONTEXT_DEACTIVATE ); + ok_ret( 1, ImmSetActiveContext( hwnd, params.himc[0], FALSE ) ); + CHECK_CALLED( WM_IME_SETCONTEXT_DEACTIVATE ); + + SET_ENABLE( WM_IME_SETCONTEXT_DEACTIVATE, FALSE ); + SET_ENABLE( WM_IME_SETCONTEXT_ACTIVATE, FALSE ); + + /* ImmSetOpenStatus should fail with cross thread HIMC */ + + ok_ret( 1, ImmSetOpenStatus( himc[1], 0xdeadbeef ) ); + ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( himc[1] ) ); + + ok_ret( 0, ImmSetOpenStatus( params.himc[0], TRUE ) ); + ok_ret( 0, ImmSetOpenStatus( params.himc[1], TRUE ) ); + ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( params.himc[0] ) ); + ok_ret( (int)0xfeedcafe, ImmGetOpenStatus( params.himc[1] ) ); + ok_ret( 0, ImmSetOpenStatus( params.himc[0], FALSE ) ); + ok_ret( (int)0xdeadbeef, ImmGetOpenStatus( params.himc[0] ) ); + + /* ImmSetConversionStatus should fail with cross thread HIMC */ + + ok_ret( 1, ImmGetConversionStatus( himc[1], &conversion, &sentence ) ); + ok_ret( 1, ImmSetConversionStatus( himc[1], conversion, sentence ) ); + + ok_ret( 1, ImmGetConversionStatus( params.himc[0], &conversion, &sentence ) ); + ok_ret( 1, ImmGetConversionStatus( params.himc[1], &conversion, &sentence ) ); + ok_ret( 0, ImmSetConversionStatus( params.himc[0], conversion, sentence ) ); + ok_ret( 0, ImmSetConversionStatus( params.himc[1], conversion, sentence ) ); + + /* ImmSetCompositionFont(W|A) should fail with cross thread HIMC */ + + ok_ret( 1, ImmSetCompositionFontA( himc[1], &fontA ) ); + ok_ret( 1, ImmGetCompositionFontA( himc[1], &fontA ) ); + ok_ret( 1, ImmSetCompositionFontW( himc[1], &fontW ) ); + ok_ret( 1, ImmGetCompositionFontW( himc[1], &fontW ) ); + + ok_ret( 0, ImmSetCompositionFontA( params.himc[0], &fontA ) ); + ok_ret( 0, ImmSetCompositionFontA( params.himc[1], &fontA ) ); + ok_ret( 1, ImmGetCompositionFontA( params.himc[0], &fontA ) ); + ok_ret( 1, ImmGetCompositionFontA( params.himc[1], &fontA ) ); + ok_ret( 0, ImmSetCompositionFontW( params.himc[0], &fontW ) ); + ok_ret( 0, ImmSetCompositionFontW( params.himc[1], &fontW ) ); + ok_ret( 1, ImmGetCompositionFontW( params.himc[0], &fontW ) ); + ok_ret( 1, ImmGetCompositionFontW( params.himc[1], &fontW ) ); + + /* ImmSetCompositionString(W|A) should fail with cross thread HIMC */ + + ok_ret( 10, ImmGetCompositionStringA( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + ok_ret( 20, ImmGetCompositionStringW( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + ok_ret( 1, ImmSetCompositionStringA( himc[1], SCS_SETSTR, "a", 2, NULL, 0 ) ); + ok_ret( 1, ImmSetCompositionStringW( himc[1], SCS_SETSTR, L"a", 4, NULL, 0 ) ); + + ok_ret( 0, ImmSetCompositionStringA( params.himc[0], SCS_SETSTR, "a", 2, NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringA( params.himc[1], SCS_SETSTR, "a", 2, NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringW( params.himc[0], SCS_SETSTR, L"a", 4, NULL, 0 ) ); + ok_ret( 0, ImmSetCompositionStringW( params.himc[1], SCS_SETSTR, L"a", 4, NULL, 0 ) ); + ok_ret( 10, ImmGetCompositionStringA( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + ok_ret( 0, ImmGetCompositionStringA( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + ok_ret( 20, ImmGetCompositionStringW( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + ok_ret( 0, ImmGetCompositionStringW( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + + /* ImmSetCompositionWindow should fail with cross thread HIMC */ + + ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) ); + ok_ret( 1, ImmGetCompositionWindow( himc[1], &composition ) ); + + ok_ret( 0, ImmSetCompositionWindow( params.himc[0], &composition ) ); + ok_ret( 0, ImmSetCompositionWindow( params.himc[1], &composition ) ); + ok_ret( 1, ImmGetCompositionWindow( params.himc[0], &composition ) ); + ok_ret( 1, ImmGetCompositionWindow( params.himc[1], &composition ) ); + + /* ImmSetCandidateWindow should fail with cross thread HIMC */ + + ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) ); + ok_ret( 1, ImmGetCandidateWindow( himc[1], 0, &candidate ) ); + + ok_ret( 1, ImmGetCandidateWindow( params.himc[0], 1, &candidate ) ); + ok_ret( 1, ImmGetCandidateWindow( params.himc[1], 1, &candidate ) ); + ok_ret( 0, ImmSetCandidateWindow( params.himc[0], &candidate ) ); + ok_ret( 0, ImmSetCandidateWindow( params.himc[1], &candidate ) ); + + /* ImmSetStatusWindowPos should fail with cross thread HIMC */ + + ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) ); + ok_ret( 1, ImmGetStatusWindowPos( himc[1], &pos ) ); + + ok_ret( 0, ImmSetStatusWindowPos( params.himc[0], &pos ) ); + ok_ret( 0, ImmSetStatusWindowPos( params.himc[1], &pos ) ); + ok_ret( 1, ImmGetStatusWindowPos( params.himc[0], &pos ) ); + ok_ret( 1, ImmGetStatusWindowPos( params.himc[1], &pos ) ); + + /* ImmGenerateMessage should fail with cross thread HIMC */ + + ok_ret( 1, ImmGenerateMessage( himc[1] ) ); + + todo_wine ok_ret( 0, ImmGenerateMessage( params.himc[0] ) ); + todo_wine ok_ret( 0, ImmGenerateMessage( params.himc[1] ) ); + + /* ImmAssociateContext should fail with cross thread HWND or HIMC */ + + tmp_himc = ImmAssociateContext( hwnd, params.himc[0] ); + ok_eq( NULL, tmp_himc, HIMC, "%p" ); + tmp_himc = ImmGetContext( hwnd ); + ok_eq( himc[0], tmp_himc, HIMC, "%p" ); + ok_ret( 1, ImmReleaseContext( hwnd, tmp_himc ) ); + + tmp_himc = ImmAssociateContext( hwnd, params.himc[1] ); + ok_eq( NULL, tmp_himc, HIMC, "%p" ); + tmp_himc = ImmGetContext( hwnd ); + ok_eq( himc[0], tmp_himc, HIMC, "%p" ); + ok_ret( 1, ImmReleaseContext( hwnd, tmp_himc ) ); + + tmp_himc = ImmAssociateContext( params.hwnd, params.himc[1] ); + ok_eq( NULL, tmp_himc, HIMC, "%p" ); + tmp_himc = ImmGetContext( params.hwnd ); + ok_eq( params.himc[0], tmp_himc, HIMC, "%p" ); + ok_ret( 1, ImmReleaseContext( params.hwnd, tmp_himc ) ); + + /* ImmAssociateContext should succeed with cross thread HWND and NULL HIMC */ + + tmp_himc = ImmAssociateContext( params.hwnd, NULL ); + ok_eq( params.himc[0], tmp_himc, HIMC, "%p" ); + tmp_himc = ImmGetContext( params.hwnd ); + ok_eq( NULL, tmp_himc, HIMC, "%p" ); + + /* ImmReleaseContext / ImmDestroyContext should fail with cross thread HIMC */ + + ok_ret( 1, ImmReleaseContext( params.hwnd, params.himc[0] ) ); + ok_ret( 0, ImmDestroyContext( params.himc[1] ) ); + + /* ImmGetContext should fail with another process HWND */ + + tmp_himc = ImmGetContext( GetDesktopWindow() ); + ok_eq( NULL, tmp_himc, HIMC, "%p" ); + + ok_ret( 0, SendMessageW( params.hwnd, WM_CLOSE, 0, 0 ) ); + ok_ret( 1, PostThreadMessageW( tid, WM_QUIT, 1, 0 ) ); + ok_ret( 0, WaitForSingleObject( thread, 5000 ) ); + ok_ret( 1, CloseHandle( thread ) ); + ok_ret( 1, CloseHandle( params.event ) ); + + ok_ret( 1, ImmReleaseContext( hwnd, himc[0] ) ); + ok_ret( 1, ImmDestroyContext( himc[1] ) ); } static void test_ImmIsUIMessage(void) @@ -6001,7 +6088,7 @@ START_TEST(imm32) test_ImmIME(); test_ImmAssociateContextEx(); test_NtUserAssociateInputContext(); - test_ImmThreads(); + test_cross_thread_himc(); test_ImmIsUIMessage(); test_ImmGetContext(); test_ImmDefaultHwnd(); From 3893f5cdaf5cd75a1e5a11427aec723a8ad9c32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 11:33:24 +0200 Subject: [PATCH 236/758] imm32: Serialize ImeInquire / ImeDestroy calls. (cherry picked from commit 3cd1dc5273b9118e54ac113b52336da0f17e5f32) --- dlls/imm32/imm.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 25d1df47a1c..268d9142732 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -441,11 +441,14 @@ BOOL WINAPI ImmFreeLayout( HKL hkl ) EnterCriticalSection( &ime_cs ); if ((ime = find_ime_from_hkl( hkl )) && ime->refcount) ime = NULL; - if (ime) list_remove( &ime->entry ); + if (ime) + { + list_remove( &ime->entry ); + if (!ime->pImeDestroy( 0 )) WARN( "ImeDestroy failed\n" ); + } LeaveCriticalSection( &ime_cs ); if (!ime) return TRUE; - if (!ime->pImeDestroy( 0 )) WARN( "ImeDestroy failed\n" ); FreeLibrary( ime->module ); free( ime ); return TRUE; @@ -460,12 +463,11 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) TRACE( "hkl %p\n", hkl ); EnterCriticalSection( &ime_cs ); - ime = find_ime_from_hkl( hkl ); - LeaveCriticalSection( &ime_cs ); - if (ime) return TRUE; - - if (!(ime = calloc( 1, sizeof(*ime) ))) return FALSE; - ime->hkl = hkl; + if ((ime = find_ime_from_hkl( hkl )) || !(ime = calloc( 1, sizeof(*ime) ))) + { + LeaveCriticalSection( &ime_cs ); + return !!ime; + } if (!ImmGetIMEFileNameW( hkl, buffer, MAX_PATH )) use_default_ime = TRUE; else if (!(ime->module = LoadLibraryW( buffer ))) use_default_ime = TRUE; @@ -481,6 +483,7 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f )) && \ !(ime->p##f = use_default_ime ? (void *)f : NULL)) \ { \ + LeaveCriticalSection( &ime_cs ); \ WARN( "Can't find function %s in HKL %p IME\n", #f, hkl ); \ goto failed; \ } @@ -502,12 +505,16 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) LOAD_FUNCPTR( ImeGetImeMenuItems ); #undef LOAD_FUNCPTR - if (!ime->pImeInquire( &ime->info, buffer, 0 )) goto failed; + ime->hkl = hkl; + if (!ime->pImeInquire( &ime->info, buffer, 0 )) + { + LeaveCriticalSection( &ime_cs ); + goto failed; + } if (ime_is_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) ); else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->ui_class, ARRAY_SIZE(ime->ui_class) ); - EnterCriticalSection( &ime_cs ); list_add_tail( &ime_list, &ime->entry ); LeaveCriticalSection( &ime_cs ); From 88c33be81e7dbc507b6165e44ed06b23a6aaf450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Apr 2023 21:27:00 +0200 Subject: [PATCH 237/758] imm32: Use INPUTCONTEXT directly in ImmGetOpenStatus. (cherry picked from commit 0af582947632fd827bd0edeb348aa29162e3c470) --- dlls/imm32/imm.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 268d9142732..753213b623f 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1919,20 +1919,18 @@ UINT WINAPI ImmGetIMEFileNameW( HKL hkl, WCHAR *buffer, UINT length ) /*********************************************************************** * ImmGetOpenStatus (IMM32.@) */ -BOOL WINAPI ImmGetOpenStatus(HIMC hIMC) +BOOL WINAPI ImmGetOpenStatus( HIMC himc ) { - struct imc *data = get_imc_data( hIMC ); - static int i; + INPUTCONTEXT *ctx; + BOOL status; - if (!data) - return FALSE; - - TRACE("(%p): semi-stub\n", hIMC); + TRACE( "himc %p\n", himc ); - if (!i++) - FIXME("(%p): semi-stub\n", hIMC); + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + status = ctx->fOpen; + ImmUnlockIMC( himc ); - return data->IMC.fOpen; + return status; } /*********************************************************************** From 7cae81e4d1dfe5b6e91730325c1f2ceffd5347d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 3 Apr 2023 17:39:21 +0200 Subject: [PATCH 238/758] imm32: Use INPUTCONTEXT directly in ImmSetOpenStatus. (cherry picked from commit 374db20a5d011ad909a61d6857ead7a6261231ec) --- dlls/imm32/imm.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 753213b623f..a17869a1c94 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2605,30 +2605,27 @@ BOOL WINAPI ImmSetConversionStatus( /*********************************************************************** * ImmSetOpenStatus (IMM32.@) */ -BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen) +BOOL WINAPI ImmSetOpenStatus( HIMC himc, BOOL status ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; HWND ui_hwnd; - TRACE("%p %d\n", hIMC, fOpen); - - if (!data) - { - SetLastError(ERROR_INVALID_HANDLE); - return FALSE; - } + TRACE( "himc %p, status %u\n", himc, status ); - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; - if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)hIMC ); + if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)himc ); - if (!fOpen != !data->IMC.fOpen) + if (!status != !ctx->fOpen) { - data->IMC.fOpen = fOpen; - ImmNotifyIME( hIMC, NI_CONTEXTUPDATED, 0, IMC_SETOPENSTATUS); - imc_notify_ime( data, IMN_SETOPENSTATUS, 0 ); + ctx->fOpen = status; + ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETOPENSTATUS ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETOPENSTATUS, 0 ); } + ImmUnlockIMC( himc ); + return TRUE; } From 26824a0c2df8a60954dbf50c335a3c355bf2e777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Apr 2023 19:47:26 +0200 Subject: [PATCH 239/758] imm32: Cache INPUTCONTEXT values for every IME. (cherry picked from commit 6e51928ae5fbd7a7d199e2d12f25b3206e8cd94b) --- dlls/imm32/imm.c | 89 ++++++++++++++++++++++++++++++++++++---- dlls/imm32/tests/imm32.c | 18 ++------ 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index a17869a1c94..81444ad420c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -39,6 +39,13 @@ UINT WM_MSIME_RECONVERT; UINT WM_MSIME_QUERYPOSITION; UINT WM_MSIME_DOCUMENTFEED; +struct imc_entry +{ + HIMC himc; + INPUTCONTEXT context; + struct list entry; +}; + struct ime { LONG refcount; /* guarded by ime_cs */ @@ -49,6 +56,7 @@ struct ime IMEINFO info; WCHAR ui_class[17]; + struct list input_contexts; BOOL (WINAPI *pImeInquire)(IMEINFO *, void *, DWORD); BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *); @@ -435,16 +443,21 @@ static struct ime *find_ime_from_hkl( HKL hkl ) BOOL WINAPI ImmFreeLayout( HKL hkl ) { + struct imc_entry *imc_entry, *imc_next; struct ime *ime; TRACE( "hkl %p\n", hkl ); EnterCriticalSection( &ime_cs ); - if ((ime = find_ime_from_hkl( hkl )) && ime->refcount) ime = NULL; - if (ime) + if ((ime = find_ime_from_hkl( hkl ))) { list_remove( &ime->entry ); if (!ime->pImeDestroy( 0 )) WARN( "ImeDestroy failed\n" ); + LIST_FOR_EACH_ENTRY_SAFE( imc_entry, imc_next, &ime->input_contexts, struct imc_entry, entry ) + { + ImmDestroyIMCC( imc_entry->context.hPrivate ); + free( imc_entry ); + } } LeaveCriticalSection( &ime_cs ); if (!ime) return TRUE; @@ -514,6 +527,7 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) if (ime_is_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) ); else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->ui_class, ARRAY_SIZE(ime->ui_class) ); + list_init( &ime->input_contexts ); list_add_tail( &ime_list, &ime->entry ); LeaveCriticalSection( &ime_cs ); @@ -562,13 +576,63 @@ static void ime_release( struct ime *ime ) LeaveCriticalSection( &ime_cs ); } +static void ime_save_input_context( struct ime *ime, HIMC himc, INPUTCONTEXT *ctx ) +{ + static INPUTCONTEXT default_input_context = + { + .cfCandForm = {{.dwIndex = -1}, {.dwIndex = -1}, {.dwIndex = -1}, {.dwIndex = -1}} + }; + const INPUTCONTEXT old = *ctx; + struct imc_entry *entry; + + *ctx = default_input_context; + ctx->hWnd = old.hWnd; + ctx->hMsgBuf = old.hMsgBuf; + ctx->hCompStr = old.hCompStr; + ctx->hCandInfo = old.hCandInfo; + ctx->hGuideLine = old.hGuideLine; + if (!(ctx->hPrivate = ImmCreateIMCC( ime->info.dwPrivateDataSize ))) + WARN( "Failed to allocate IME private data\n" ); + + if (!(entry = malloc( sizeof(*entry) ))) return; + entry->himc = himc; + entry->context = *ctx; + + EnterCriticalSection( &ime_cs ); + + /* reference the IME the first time the input context cache is used + * in the same way Windows does it, so it doesn't get destroyed and + * INPUTCONTEXT cache lost when keyboard layout is changed + */ + if (list_empty( &ime->input_contexts )) ime->refcount++; + + list_add_tail( &ime->input_contexts, &entry->entry ); + LeaveCriticalSection( &ime_cs ); +} + +static INPUTCONTEXT *ime_find_input_context( struct ime *ime, HIMC himc ) +{ + struct imc_entry *entry; + + EnterCriticalSection( &ime_cs ); + LIST_FOR_EACH_ENTRY( entry, &ime->input_contexts, struct imc_entry, entry ) + if (entry->himc == himc) break; + LeaveCriticalSection( &ime_cs ); + + if (&entry->entry == &ime->input_contexts) return NULL; + return &entry->context; +} + static void imc_release_ime( struct imc *imc, struct ime *ime ) { + INPUTCONTEXT *ctx; + if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd ); imc->ui_hwnd = NULL; ime->pImeSelect( imc->handle, FALSE ); + + if ((ctx = ime_find_input_context( ime, imc->handle ))) *ctx = imc->IMC; ime_release( ime ); - ImmDestroyIMCC( imc->IMC.hPrivate ); } static struct ime *imc_select_ime( struct imc *imc ) @@ -587,10 +651,11 @@ static struct ime *imc_select_ime( struct imc *imc ) WARN( "Failed to acquire IME for HKL %p\n", hkl ); else { - if (!(imc->IMC.hPrivate = ImmCreateIMCC( imc->ime->info.dwPrivateDataSize ))) - WARN( "Failed to allocate IME private data for IMC %p\n", imc ); - imc->IMC.fdwConversion = imc->ime->info.fdwConversionCaps; - imc->IMC.fdwSentence = imc->ime->info.fdwSentenceCaps; + INPUTCONTEXT *ctx; + + if ((ctx = ime_find_input_context( imc->ime, imc->handle ))) imc->IMC = *ctx; + else ime_save_input_context( imc->ime, imc->handle, &imc->IMC ); + imc->ime->pImeSelect( imc->handle, TRUE ); } @@ -690,14 +755,20 @@ static void IMM_FreeThreadData(void) static void IMM_FreeAllImmHkl(void) { - struct ime *ime, *next; + struct ime *ime, *ime_next; - LIST_FOR_EACH_ENTRY_SAFE( ime, next, &ime_list, struct ime, entry ) + LIST_FOR_EACH_ENTRY_SAFE( ime, ime_next, &ime_list, struct ime, entry ) { + struct imc_entry *imc_entry, *imc_next; list_remove( &ime->entry ); ime->pImeDestroy( 1 ); FreeLibrary( ime->module ); + LIST_FOR_EACH_ENTRY_SAFE( imc_entry, imc_next, &ime->input_contexts, struct imc_entry, entry ) + { + ImmDestroyIMCC( imc_entry->context.hPrivate ); + free( imc_entry ); + } free( ime ); } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 827a1326e3e..88b167c701d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4351,8 +4351,8 @@ static void test_ImmSetConversionStatus(void) todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" ); ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); ok_ret( 1, ImmActivateLayout( hkl ) ); - todo_wine ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" ); - todo_wine ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); + ok_eq( ~0, ctx->fdwConversion, UINT, "%#x" ); + ok_eq( ~0, ctx->fdwSentence, UINT, "%#x" ); ok_ret( 1, ImmActivateLayout( default_hkl ) ); todo_wine ok_eq( 0x200, ctx->fdwConversion, UINT, "%#x" ); ok_eq( old_sentence, ctx->fdwSentence, UINT, "%#x" ); @@ -4506,8 +4506,8 @@ static void test_ImmSetOpenStatus(void) ok_ret( 1, ImmActivateLayout( default_hkl ) ); status = ImmGetOpenStatus( default_himc ); - todo_wine ok_eq( old_status, status, UINT, "%#x" ); - todo_wine ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); + ok_eq( old_status, status, UINT, "%#x" ); + ok_eq( old_status, ctx->fOpen, UINT, "%#x" ); ok_ret( 1, ImmActivateLayout( hkl ) ); status = ImmGetOpenStatus( default_himc ); todo_wine ok_eq( 1, status, UINT, "%#x" ); @@ -4752,10 +4752,8 @@ static void test_ImmActivateLayout(void) ok_ret( 1, ImmActivateLayout( hkl ) ); ok_seq( empty_sequence ); - todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_seq( deactivate_seq ); - todo_ImeDestroy = FALSE; ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -4786,9 +4784,7 @@ static void test_ImmActivateLayout(void) CHECK_CALLED( ImeInquire ); activate_with_window_seq[1].himc = himc; ok_seq( activate_with_window_seq ); - todo_ImeInquire = TRUE; ok_ret( 1, ImmLoadIME( hkl ) ); - todo_ImeInquire = FALSE; ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); @@ -4797,9 +4793,7 @@ static void test_ImmActivateLayout(void) memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; - todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" ); - todo_ImeDestroy = FALSE; deactivate_with_window_seq[1].himc = himc; deactivate_with_window_seq[5].himc = himc; ok_seq( deactivate_with_window_seq ); @@ -4810,9 +4804,7 @@ static void test_ImmActivateLayout(void) ok_seq( empty_sequence ); - todo_ImeInquire = TRUE; ok_eq( default_hkl, ActivateKeyboardLayout( hkl, 0 ), HKL, "%p" ); - todo_ImeInquire = FALSE; ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); @@ -4820,9 +4812,7 @@ static void test_ImmActivateLayout(void) ok( !!ime_windows.ime_ui_hwnd, "missing IME UI window\n" ); ok_ret( (UINT_PTR)ime_windows.ime_hwnd, (UINT_PTR)GetParent( ime_windows.ime_ui_hwnd ) ); - todo_ImeDestroy = TRUE; /* Wine doesn't leak the IME */ ok_eq( hkl, ActivateKeyboardLayout( default_hkl, 0 ), HKL, "%p" ); - todo_ImeDestroy = FALSE; ok_eq( default_hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); process_messages(); From 29f30e38fdf2b5fcf14fb044c8f4bbed2d5a7fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 00:54:42 +0200 Subject: [PATCH 240/758] imm32: Compare open status values in ImmSetOpenStatus. (cherry picked from commit 26d2d2c438d9e5f66212b22dc2d29b7881e9ad09) --- dlls/imm32/imm.c | 2 +- dlls/imm32/tests/imm32.c | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 81444ad420c..c99feb65a45 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2688,7 +2688,7 @@ BOOL WINAPI ImmSetOpenStatus( HIMC himc, BOOL status ) if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)himc ); - if (!status != !ctx->fOpen) + if (status != ctx->fOpen) { ctx->fOpen = status; ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETOPENSTATUS ); diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 88b167c701d..8c6d441181b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4399,7 +4399,6 @@ static void test_ImmSetOpenStatus(void) { .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, - .todo = TRUE, }, {0}, }; @@ -4408,17 +4407,14 @@ static void test_ImmSetOpenStatus(void) { .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETOPENSTATUS}, - .todo = TRUE, }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, - .todo = TRUE, }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETOPENSTATUS}, - .todo = TRUE, }, {0}, }; @@ -4483,8 +4479,8 @@ static void test_ImmSetOpenStatus(void) ok_seq( set_open_status_1_seq ); status = ImmGetOpenStatus( default_himc ); - todo_wine ok_eq( 0xfeedcafe, status, UINT, "%#x" ); - todo_wine ok_eq( 0xfeedcafe, ctx->fOpen, UINT, "%#x" ); + ok_eq( 0xfeedcafe, status, UINT, "%#x" ); + ok_eq( 0xfeedcafe, ctx->fOpen, UINT, "%#x" ); ctx->hWnd = hwnd; ok_seq( empty_sequence ); @@ -4492,15 +4488,15 @@ static void test_ImmSetOpenStatus(void) ok_seq( set_open_status_2_seq ); status = ImmGetOpenStatus( default_himc ); - todo_wine ok_eq( ~0, status, UINT, "%#x" ); - todo_wine ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); + ok_eq( ~0, status, UINT, "%#x" ); + ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); ok_ret( 1, ImmSetOpenStatus( default_himc, ~0 ) ); ok_seq( empty_sequence ); status = ImmGetOpenStatus( default_himc ); - todo_wine ok_eq( ~0, status, UINT, "%#x" ); - todo_wine ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); + ok_eq( ~0, status, UINT, "%#x" ); + ok_eq( ~0, ctx->fOpen, UINT, "%#x" ); /* status is cached between IME activations */ From 92d6cfc834baa52156ce0b26fc28e9e47dc452df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Apr 2023 21:43:27 +0200 Subject: [PATCH 241/758] imm32: Use INPUTCONTEXT directly in ImmGetConversionStatus. (cherry picked from commit 67ddc3146cb847e3161567a870230bba13d9c3f5) --- dlls/imm32/imm.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index c99feb65a45..b2dc58a95ab 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1844,20 +1844,16 @@ DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDI /*********************************************************************** * ImmGetConversionStatus (IMM32.@) */ -BOOL WINAPI ImmGetConversionStatus( - HIMC hIMC, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence) +BOOL WINAPI ImmGetConversionStatus( HIMC himc, DWORD *conversion, DWORD *sentence ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; - TRACE("%p %p %p\n", hIMC, lpfdwConversion, lpfdwSentence); + TRACE( "himc %p, conversion %p, sentence %p\n", himc, conversion, sentence ); - if (!data) - return FALSE; - - if (lpfdwConversion) - *lpfdwConversion = data->IMC.fdwConversion; - if (lpfdwSentence) - *lpfdwSentence = data->IMC.fdwSentence; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + if (conversion) *conversion = ctx->fdwConversion; + if (sentence) *sentence = ctx->fdwSentence; + ImmUnlockIMC( himc ); return TRUE; } From baa2b458dca126d73338f83d7bca4713ef97b852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 01:01:37 +0200 Subject: [PATCH 242/758] imm32: Use INPUTCONTEXT directly in ImmSetConversionStatus. (cherry picked from commit 1cd71e92be6ed57621ea545fcfbd46da7cfe7b58) --- dlls/imm32/imm.c | 41 +++++++++++++++++++--------------------- dlls/imm32/tests/imm32.c | 1 - 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index b2dc58a95ab..d29684f34a2 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2635,37 +2635,34 @@ BOOL WINAPI ImmSetCompositionWindow( /*********************************************************************** * ImmSetConversionStatus (IMM32.@) */ -BOOL WINAPI ImmSetConversionStatus( - HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence) +BOOL WINAPI ImmSetConversionStatus( HIMC himc, DWORD conversion, DWORD sentence ) { - DWORD oldConversion, oldSentence; - struct imc *data = get_imc_data( hIMC ); - - TRACE("%p %ld %ld\n", hIMC, fdwConversion, fdwSentence); + DWORD old_conversion, old_sentence; + INPUTCONTEXT *ctx; - if (!data) - { - SetLastError(ERROR_INVALID_HANDLE); - return FALSE; - } + TRACE( "himc %p, conversion %#lx, sentence %#lx\n", himc, conversion, sentence ); - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; - if ( fdwConversion != data->IMC.fdwConversion ) + if (conversion != ctx->fdwConversion) { - oldConversion = data->IMC.fdwConversion; - data->IMC.fdwConversion = fdwConversion; - ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, oldConversion, IMC_SETCONVERSIONMODE); - imc_notify_ime( data, IMN_SETCONVERSIONMODE, 0 ); + old_conversion = ctx->fdwConversion; + ctx->fdwConversion = conversion; + ImmNotifyIME( himc, NI_CONTEXTUPDATED, old_conversion, IMC_SETCONVERSIONMODE ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCONVERSIONMODE, 0 ); } - if ( fdwSentence != data->IMC.fdwSentence ) + + if (sentence != ctx->fdwSentence) { - oldSentence = data->IMC.fdwSentence; - data->IMC.fdwSentence = fdwSentence; - ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, oldSentence, IMC_SETSENTENCEMODE); - imc_notify_ime( data, IMN_SETSENTENCEMODE, 0 ); + old_sentence = ctx->fdwSentence; + ctx->fdwSentence = sentence; + ImmNotifyIME( himc, NI_CONTEXTUPDATED, old_sentence, IMC_SETSENTENCEMODE ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETSENTENCEMODE, 0 ); } + ImmUnlockIMC( himc ); + return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 8c6d441181b..bbfae7e65d1 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4219,7 +4219,6 @@ static void test_ImmSetConversionStatus(void) .hkl = expect_ime, .himc = default_himc, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0xdeadbeef, .value = IMC_SETCONVERSIONMODE}, }, - {.todo = TRUE}, /* spurious calls */ {0}, }; const struct ime_call set_conversion_status_2_seq[] = From 229519088d0f6caecbf6f349eebef0728b7be703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 13 Apr 2023 10:41:51 +0200 Subject: [PATCH 243/758] include: Add INPUTCONTEXT fdwInit flags definitions. (cherry picked from commit e1645155491c4195d970c59af5ce3832613db20e) --- include/immdev.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/immdev.h b/include/immdev.h index 92f2a47c167..4141e9350c0 100644 --- a/include/immdev.h +++ b/include/immdev.h @@ -134,6 +134,13 @@ DWORD WINAPI ImmGetIMCCSize(HIMCC); #define IMMGWL_IMC 0 #define IMMGWL_PRIVATE (sizeof(LONG_PTR)) +#define INIT_STATUSWNDPOS 0x00000001 +#define INIT_CONVERSION 0x00000002 +#define INIT_SENTENCE 0x00000004 +#define INIT_LOGFONT 0x00000008 +#define INIT_COMPFORM 0x00000010 +#define INIT_SOFTKBDPOS 0x00000020 + /* IME Property bits */ #define IME_PROP_END_UNLOAD 0x0001 #define IME_PROP_KBD_CHAR_FIRST 0x0002 From 8397dca8b36b053410b6e2056c6c57f57ea9a375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 13 Apr 2023 10:01:15 +0200 Subject: [PATCH 244/758] imm32/tests: Add some Imm(Get|Set)CompositionWindow tests. (cherry picked from commit 7a3991913b8b3339282f9835d2361c2c3f0a2ce6) --- dlls/imm32/tests/imm32.c | 113 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index bbfae7e65d1..c3c68d1e91b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -168,6 +168,14 @@ static void check_candidate_form_( int line, CANDIDATEFORM *form, const CANDIDAT check_member_rect_( __FILE__, line, *form, *expect, rcArea ); } +#define check_composition_form( a, b ) check_composition_form_( __LINE__, a, b ) +static void check_composition_form_( int line, COMPOSITIONFORM *form, const COMPOSITIONFORM *expect ) +{ + check_member_( __FILE__, line, *form, *expect, "%#lx", dwStyle ); + check_member_point_( __FILE__, line, *form, *expect, ptCurrentPos ); + check_member_rect_( __FILE__, line, *form, *expect, rcArea ); +} + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE @@ -6007,6 +6015,110 @@ static void test_ImmGetCompositionString( BOOL unicode ) SET_ENABLE( ImeSetCompositionString, FALSE ); } +static void test_ImmSetCompositionWindow(void) +{ + struct ime_call set_composition_window_0_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW}, + .todo = TRUE + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONWINDOW}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONWINDOW}, + }, + {0}, + }; + struct ime_call set_composition_window_1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW}, + .todo = TRUE + }, + {.todo = TRUE}, + }; + COMPOSITIONFORM comp_form, expect_form = + { + .dwStyle = 0xfeedcafe, + .ptCurrentPos = {.x = 123, .y = 456}, + .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4}, + }; + INPUTCONTEXT *ctx; + HIMC himc; + HKL hkl; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = wineime_hkl)) return; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + set_composition_window_0_seq[0].himc = himc; + set_composition_window_1_seq[0].himc = himc; + + ctx->cfCompForm = expect_form; + ctx->fdwInit = ~INIT_COMPFORM; + memset( &comp_form, 0xcd, sizeof(comp_form) ); + todo_wine ok_ret( 0, ImmGetCompositionWindow( himc, &comp_form ) ); + todo_wine ok_eq( 0xcdcdcdcd, comp_form.dwStyle, UINT, "%#x" ); + ctx->fdwInit = INIT_COMPFORM; + ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) ); + check_composition_form( &comp_form, &expect_form ); + ok_seq( empty_sequence ); + + ctx->hWnd = hwnd; + ctx->fdwInit = 0; + memset( &comp_form, 0xcd, sizeof(comp_form) ); + ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) ); + ok_seq( set_composition_window_0_seq ); + todo_wine ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" ); + check_composition_form( &ctx->cfCompForm, &comp_form ); + + ok_ret( 1, ImmSetCompositionWindow( himc, &expect_form ) ); + ok_seq( set_composition_window_0_seq ); + check_composition_form( &ctx->cfCompForm, &expect_form ); + + ctx->cfCompForm = expect_form; + ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) ); + check_composition_form( &comp_form, &expect_form ); + ok_seq( empty_sequence ); + + ctx->hWnd = 0; + memset( &comp_form, 0xcd, sizeof(comp_form) ); + ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) ); + ok_seq( set_composition_window_1_seq ); + check_composition_form( &ctx->cfCompForm, &comp_form ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( default_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -6063,6 +6175,7 @@ START_TEST(imm32) test_ImmGetCompositionString( TRUE ); test_ImmGetCompositionString( FALSE ); + test_ImmSetCompositionWindow(); if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); From bed848bc34ea5c4003808ad8aa954ddae45519f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 23:25:29 +0200 Subject: [PATCH 245/758] imm32/tests: Add some Imm(Get|Set)StatusWindowPos tests. (cherry picked from commit f35cb95d5f4a918234f5d7b837eea6c99d79ed40) --- dlls/imm32/tests/imm32.c | 99 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index c3c68d1e91b..60ecf7b050b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6119,6 +6119,104 @@ static void test_ImmSetCompositionWindow(void) ime_call_count = 0; } +static void test_ImmSetStatusWindowPos(void) +{ + struct ime_call set_status_window_pos_0_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSTATUSWINDOWPOS}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETSTATUSWINDOWPOS}, + }, + {0}, + }; + struct ime_call set_status_window_pos_1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS}, + }, + {.todo = TRUE}, + }; + INPUTCONTEXT *ctx; + POINT pos; + HIMC himc; + HKL hkl; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = wineime_hkl)) return; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + set_status_window_pos_0_seq[0].himc = himc; + set_status_window_pos_1_seq[0].himc = himc; + + memset( &pos, 0xcd, sizeof(pos) ); + ctx->ptStatusWndPos.x = 0xdeadbeef; + ctx->ptStatusWndPos.y = 0xfeedcafe; + ctx->fdwInit = ~INIT_STATUSWNDPOS; + todo_wine ok_ret( 0, ImmGetStatusWindowPos( himc, &pos ) ); + todo_wine ok_eq( 0xcdcdcdcd, pos.x, UINT, "%u" ); + todo_wine ok_eq( 0xcdcdcdcd, pos.y, UINT, "%u" ); + ctx->fdwInit = INIT_STATUSWNDPOS; + ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) ); + ok_eq( 0xdeadbeef, pos.x, UINT, "%u" ); + ok_eq( 0xfeedcafe, pos.y, UINT, "%u" ); + ok_seq( empty_sequence ); + + pos.x = 123; + pos.y = 456; + ctx->hWnd = hwnd; + ctx->fdwInit = 0; + ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) ); + ok_seq( set_status_window_pos_0_seq ); + todo_wine ok_eq( INIT_STATUSWNDPOS, ctx->fdwInit, UINT, "%u" ); + + ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) ); + ok_seq( set_status_window_pos_0_seq ); + ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) ); + ok_eq( 123, pos.x, UINT, "%u" ); + ok_eq( 123, ctx->ptStatusWndPos.x, UINT, "%u" ); + ok_eq( 456, pos.y, UINT, "%u" ); + ok_eq( 456, ctx->ptStatusWndPos.y, UINT, "%u" ); + ok_seq( empty_sequence ); + + ctx->hWnd = 0; + ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) ); + ok_seq( set_status_window_pos_1_seq ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( default_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -6176,6 +6274,7 @@ START_TEST(imm32) test_ImmGetCompositionString( TRUE ); test_ImmGetCompositionString( FALSE ); test_ImmSetCompositionWindow(); + test_ImmSetStatusWindowPos(); if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); From 6cf83f33b51ccdd46b36c6b4c39eb2db6335f760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 30 Mar 2023 23:06:05 +0200 Subject: [PATCH 246/758] imm32/tests: Add some Imm(Get|Set)CompositionFont tests. (cherry picked from commit 7ddc95d4aa7c0fa4db0e928d7af59f8886ace3f8) --- dlls/imm32/tests/imm32.c | 205 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 60ecf7b050b..8202d1f7c1f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -97,6 +97,12 @@ extern BOOL WINAPI ImmActivateLayout(HKL); #define check_member_wstr( val, exp, member ) \ check_member_wstr_( __FILE__, __LINE__, val, exp, member ) +#define check_member_str_( file, line, val, exp, member ) \ + ok_(file, line)( !strcmp( (val).member, (exp).member ), "got " #member " %s\n", \ + debugstr_a((val).member) ) +#define check_member_str( val, exp, member ) \ + check_member_str_( __FILE__, __LINE__, val, exp, member ) + #define check_member_point_( file, line, val, exp, member ) \ ok_(file, line)( !memcmp( &(val).member, &(exp).member, sizeof(POINT) ), \ "got " #member " %s\n", wine_dbgstr_point( &(val).member ) ) @@ -176,6 +182,44 @@ static void check_composition_form_( int line, COMPOSITIONFORM *form, const COMP check_member_rect_( __FILE__, line, *form, *expect, rcArea ); } +#define check_logfont_w( a, b ) check_logfont_w_( __LINE__, a, b, FALSE ) +static void check_logfont_w_( int line, LOGFONTW *font, const LOGFONTW *expect, BOOL todo ) +{ + check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfEscapement ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfOrientation ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfWeight ); + check_member_( __FILE__, line, *font, *expect, "%u", lfItalic ); + check_member_( __FILE__, line, *font, *expect, "%u", lfUnderline ); + check_member_( __FILE__, line, *font, *expect, "%u", lfStrikeOut ); + check_member_( __FILE__, line, *font, *expect, "%u", lfCharSet ); + check_member_( __FILE__, line, *font, *expect, "%u", lfOutPrecision ); + check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision ); + check_member_( __FILE__, line, *font, *expect, "%u", lfQuality ); + check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily ); + todo_wine_if(todo) check_member_wstr_( __FILE__, line, *font, *expect, lfFaceName ); +} + +#define check_logfont_a( a, b ) check_logfont_a_( __LINE__, a, b, FALSE ) +static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect, BOOL todo ) +{ + check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfEscapement ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfOrientation ); + check_member_( __FILE__, line, *font, *expect, "%lu", lfWeight ); + check_member_( __FILE__, line, *font, *expect, "%u", lfItalic ); + check_member_( __FILE__, line, *font, *expect, "%u", lfUnderline ); + check_member_( __FILE__, line, *font, *expect, "%u", lfStrikeOut ); + check_member_( __FILE__, line, *font, *expect, "%u", lfCharSet ); + check_member_( __FILE__, line, *font, *expect, "%u", lfOutPrecision ); + check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision ); + check_member_( __FILE__, line, *font, *expect, "%u", lfQuality ); + check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily ); + todo_wine_if(todo) check_member_str_( __FILE__, line, *font, *expect, lfFaceName ); +} + #define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE, enabled_ ## func = FALSE @@ -1009,6 +1053,7 @@ static DWORD WINAPI test_cross_thread_himc_thread( void *arg ) COMPOSITIONFORM composition = {0}; INPUTCONTEXT *contexts[2]; HIMC himc[2], tmp_himc; + LOGFONTW fontW = {0}; HWND hwnd, tmp_hwnd; POINT pos = {0}; MSG msg; @@ -1041,6 +1086,8 @@ static DWORD WINAPI test_cross_thread_himc_thread( void *arg ) ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) ); ok_ret( 1, ImmSetStatusWindowPos( himc[0], &pos ) ); ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) ); + ok_ret( 1, ImmSetCompositionFontW( himc[0], &fontW ) ); + ok_ret( 1, ImmSetCompositionFontW( himc[1], &fontW ) ); params->hwnd = hwnd; params->himc[0] = himc[0]; @@ -6217,6 +6264,162 @@ static void test_ImmSetStatusWindowPos(void) ime_call_count = 0; } +static void test_ImmSetCompositionFont( BOOL unicode ) +{ + struct ime_call set_composition_font_0_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONFONT}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCOMPOSITIONFONT}, + }, + {0}, + }; + struct ime_call set_composition_font_1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT}, + }, + {.todo = TRUE}, + }; + LOGFONTW fontW, expect_fontW = + { + .lfHeight = 1, + .lfWidth = 2, + .lfEscapement = 3, + .lfOrientation = 4, + .lfWeight = 5, + .lfItalic = 6, + .lfUnderline = 7, + .lfStrikeOut = 8, + .lfCharSet = 8, + .lfOutPrecision = 10, + .lfClipPrecision = 11, + .lfQuality = 12, + .lfPitchAndFamily = 13, + .lfFaceName = L"FontFace", + }; + LOGFONTA fontA, expect_fontA = + { + .lfHeight = 1, + .lfWidth = 2, + .lfEscapement = 3, + .lfOrientation = 4, + .lfWeight = 5, + .lfItalic = 6, + .lfUnderline = 7, + .lfStrikeOut = 8, + .lfCharSet = 8, + .lfOutPrecision = 10, + .lfClipPrecision = 11, + .lfQuality = 12, + .lfPitchAndFamily = 13, + .lfFaceName = "FontFace", + }; + INPUTCONTEXT *ctx; + HIMC himc; + HKL hkl; + + winetest_push_context( unicode ? "unicode" : "ansi" ); + + /* IME_PROP_END_UNLOAD for the IME to unload / reload. */ + ime_info.fdwProperty = IME_PROP_END_UNLOAD; + if (unicode) ime_info.fdwProperty |= IME_PROP_UNICODE; + + if (!(hkl = wineime_hkl)) goto cleanup; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + set_composition_font_0_seq[0].himc = himc; + set_composition_font_1_seq[0].himc = himc; + + memset( &fontW, 0xcd, sizeof(fontW) ); + memset( &fontA, 0xcd, sizeof(fontA) ); + + if (unicode) ctx->lfFont.W = expect_fontW; + else ctx->lfFont.A = expect_fontA; + ctx->fdwInit = ~INIT_LOGFONT; + todo_wine ok_ret( 0, ImmGetCompositionFontW( himc, &fontW ) ); + todo_wine ok_ret( 0, ImmGetCompositionFontA( himc, &fontA ) ); + ctx->fdwInit = INIT_LOGFONT; + ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); + check_logfont_w_( __LINE__, &fontW, &expect_fontW, !unicode ); + ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); + check_logfont_a_( __LINE__, &fontA, &expect_fontA, !unicode ); + + ctx->hWnd = hwnd; + ctx->fdwInit = 0; + memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) ); + ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); + todo_wine ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" ); + ok_seq( set_composition_font_0_seq ); + ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); + ok_seq( set_composition_font_0_seq ); + if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW ); + else check_logfont_a_( __LINE__, &ctx->lfFont.A, &expect_fontA, TRUE ); + + ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); + check_logfont_w( &fontW, &expect_fontW ); + ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); + check_logfont_a( &fontA, &expect_fontA ); + + ctx->hWnd = hwnd; + ctx->fdwInit = 0; + memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) ); + ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) ); + todo_wine ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" ); + ok_seq( set_composition_font_0_seq ); + ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) ); + ok_seq( set_composition_font_0_seq ); + if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW ); + else check_logfont_a_( __LINE__, &ctx->lfFont.A, &expect_fontA, TRUE ); + + ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); + check_logfont_w( &fontW, &expect_fontW ); + ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); + check_logfont_a( &fontA, &expect_fontA ); + + ctx->hWnd = 0; + ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); + ok_seq( set_composition_font_1_seq ); + ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) ); + ok_seq( set_composition_font_1_seq ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( default_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + +cleanup: + winetest_pop_context(); +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -6275,6 +6478,8 @@ START_TEST(imm32) test_ImmGetCompositionString( FALSE ); test_ImmSetCompositionWindow(); test_ImmSetStatusWindowPos(); + test_ImmSetCompositionFont( TRUE ); + test_ImmSetCompositionFont( FALSE ); if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); From 2bef0e35ed57c52de4522519283e4b6701ca6a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 13 Apr 2023 11:06:21 +0200 Subject: [PATCH 247/758] imm32/tests: Add some Imm(Get|Set)CandidateWindow tests. (cherry picked from commit 564deb8a34f4bc4054d04352e01d8665c87685a4) --- dlls/imm32/tests/imm32.c | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 8202d1f7c1f..ccdc6b8f801 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6420,6 +6420,106 @@ static void test_ImmSetCompositionFont( BOOL unicode ) winetest_pop_context(); } +static void test_ImmSetCandidateWindow(void) +{ + struct ime_call set_candidate_window_0_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCANDIDATEPOS, .lparam = 4}, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_SETCANDIDATEPOS, .lparam = 4}, + }, + {0}, + }; + struct ime_call set_candidate_window_1_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS}, + }, + {.todo = TRUE}, + }; + CANDIDATEFORM cand_form, expect_form = + { + .dwIndex = 2, .dwStyle = 0xfeedcafe, + .ptCurrentPos = {.x = 123, .y = 456}, + .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4}, + }; + INPUTCONTEXT *ctx; + HIMC himc; + HKL hkl; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = wineime_hkl)) return; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + set_candidate_window_0_seq[0].himc = himc; + set_candidate_window_1_seq[0].himc = himc; + + ctx->cfCandForm[1] = expect_form; + ctx->cfCandForm[2] = expect_form; + ctx->fdwInit = 0; + memset( &cand_form, 0xcd, sizeof(cand_form) ); + ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) ); + ok_eq( 0xcdcdcdcd, cand_form.dwStyle, UINT, "%#x" ); + todo_wine ok_ret( 1, ImmGetCandidateWindow( himc, 1, &cand_form ) ); + todo_wine check_candidate_form( &cand_form, &expect_form ); + ok_ret( 1, ImmGetCandidateWindow( himc, 2, &cand_form ) ); + check_candidate_form( &cand_form, &expect_form ); + ok_seq( empty_sequence ); + + ctx->hWnd = hwnd; + memset( &cand_form, 0xcd, sizeof(cand_form) ); + cand_form.dwIndex = 2; + ok_ret( 1, ImmSetCandidateWindow( himc, &cand_form ) ); + ok_seq( set_candidate_window_0_seq ); + check_candidate_form( &ctx->cfCandForm[2], &cand_form ); + ok_eq( 0, ctx->fdwInit, UINT, "%u" ); + + ok_ret( 1, ImmSetCandidateWindow( himc, &expect_form ) ); + ok_seq( set_candidate_window_0_seq ); + check_candidate_form( &ctx->cfCandForm[2], &expect_form ); + + ctx->hWnd = 0; + memset( &cand_form, 0xcd, sizeof(cand_form) ); + cand_form.dwIndex = 2; + ok_ret( 1, ImmSetCandidateWindow( himc, &cand_form ) ); + ok_seq( set_candidate_window_1_seq ); + check_candidate_form( &ctx->cfCandForm[2], &cand_form ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( default_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -6480,6 +6580,7 @@ START_TEST(imm32) test_ImmSetStatusWindowPos(); test_ImmSetCompositionFont( TRUE ); test_ImmSetCompositionFont( FALSE ); + test_ImmSetCandidateWindow(); if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); From a0e4f8c8f57a38a2994252db155b3f238c698114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 01:18:56 +0200 Subject: [PATCH 248/758] imm32: Use INPUTCONTEXT directly in ImmSetCompositionWindow. (cherry picked from commit 048d2f0d13011cc2d4b574bb0e42f20126cfe2de) --- dlls/imm32/imm.c | 39 ++++++++++++++++++++------------------- dlls/imm32/tests/imm32.c | 6 ++---- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index d29684f34a2..16c36c0173d 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -119,6 +119,14 @@ static struct list ime_list = LIST_INIT( ime_list ); static const WCHAR layouts_formatW[] = L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lx"; +static const char *debugstr_composition( const COMPOSITIONFORM *composition ) +{ + if (!composition) return "(null)"; + return wine_dbg_sprintf( "style %#lx, pos %s, area %s", composition->dwStyle, + wine_dbgstr_point( &composition->ptCurrentPos ), + wine_dbgstr_rect( &composition->rcArea ) ); +} + static BOOL ime_is_unicode( const struct ime *ime ) { return !!(ime->info.fdwProperty & IME_PROP_UNICODE); @@ -2595,28 +2603,19 @@ BOOL WINAPI ImmSetCompositionStringW( /*********************************************************************** * ImmSetCompositionWindow (IMM32.@) */ -BOOL WINAPI ImmSetCompositionWindow( - HIMC hIMC, LPCOMPOSITIONFORM lpCompForm) +BOOL WINAPI ImmSetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) { BOOL reshow = FALSE; - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; HWND ui_hwnd; - TRACE("(%p, %p)\n", hIMC, lpCompForm); - if (lpCompForm) - TRACE("\t%lx, %s, %s\n", lpCompForm->dwStyle, - wine_dbgstr_point(&lpCompForm->ptCurrentPos), - wine_dbgstr_rect(&lpCompForm->rcArea)); + TRACE( "himc %p, composition %s\n", himc, debugstr_composition( composition ) ); - if (!data) - { - SetLastError(ERROR_INVALID_HANDLE); - return FALSE; - } - - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; - data->IMC.cfCompForm = *lpCompForm; + ctx->cfCompForm = *composition; + ctx->fdwInit |= INIT_COMPFORM; if ((ui_hwnd = get_ime_ui_window()) && IsWindowVisible( ui_hwnd )) { @@ -2624,11 +2623,13 @@ BOOL WINAPI ImmSetCompositionWindow( ShowWindow( ui_hwnd, SW_HIDE ); } - /* FIXME: this is a partial stub */ - if (ui_hwnd && reshow) ShowWindow( ui_hwnd, SW_SHOWNOACTIVATE ); - imc_notify_ime( data, IMN_SETCOMPOSITIONWINDOW, 0 ); + ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONWINDOW ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONWINDOW, 0 ); + + ImmUnlockIMC( himc ); + return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ccdc6b8f801..45efd946d4e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6069,7 +6069,6 @@ static void test_ImmSetCompositionWindow(void) { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW}, - .todo = TRUE }, { .hkl = expect_ime, .himc = default_himc, @@ -6086,9 +6085,8 @@ static void test_ImmSetCompositionWindow(void) { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONWINDOW}, - .todo = TRUE }, - {.todo = TRUE}, + {0}, }; COMPOSITIONFORM comp_form, expect_form = { @@ -6136,7 +6134,7 @@ static void test_ImmSetCompositionWindow(void) memset( &comp_form, 0xcd, sizeof(comp_form) ); ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) ); ok_seq( set_composition_window_0_seq ); - todo_wine ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" ); + ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" ); check_composition_form( &ctx->cfCompForm, &comp_form ); ok_ret( 1, ImmSetCompositionWindow( himc, &expect_form ) ); From 71ef8066640ef438830a9e26d527926539624443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 8 Apr 2023 12:00:52 +0200 Subject: [PATCH 249/758] imm32: Use INPUTCONTEXT directly in ImmGetCompositionWindow. (cherry picked from commit e49feacdb4206ea8e90e7126aa026dbfde930c9b) --- dlls/imm32/imm.c | 15 ++++++++------- dlls/imm32/tests/imm32.c | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 16c36c0173d..31a63ecbc80 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1740,17 +1740,18 @@ LONG WINAPI ImmGetCompositionStringW( /*********************************************************************** * ImmGetCompositionWindow (IMM32.@) */ -BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm) +BOOL WINAPI ImmGetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; + BOOL ret; - TRACE("(%p, %p)\n", hIMC, lpCompForm); + TRACE( "himc %p, composition %p\n", himc, composition ); - if (!data) - return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + if ((ret = !!(ctx->fdwInit & INIT_COMPFORM))) *composition = ctx->cfCompForm; + ImmUnlockIMC( himc ); - *lpCompForm = data->IMC.cfCompForm; - return TRUE; + return ret; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 45efd946d4e..3aa50ea3582 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6122,8 +6122,8 @@ static void test_ImmSetCompositionWindow(void) ctx->cfCompForm = expect_form; ctx->fdwInit = ~INIT_COMPFORM; memset( &comp_form, 0xcd, sizeof(comp_form) ); - todo_wine ok_ret( 0, ImmGetCompositionWindow( himc, &comp_form ) ); - todo_wine ok_eq( 0xcdcdcdcd, comp_form.dwStyle, UINT, "%#x" ); + ok_ret( 0, ImmGetCompositionWindow( himc, &comp_form ) ); + ok_eq( 0xcdcdcdcd, comp_form.dwStyle, UINT, "%#x" ); ctx->fdwInit = INIT_COMPFORM; ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) ); check_composition_form( &comp_form, &expect_form ); From 7269271abf12ad163090a3c3525c27809365e378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 01:05:13 +0200 Subject: [PATCH 250/758] imm32: Use INPUTCONTEXT directly in ImmSetStatusWindowPos. (cherry picked from commit d0a88bf7de160185476f593eb50142ab9dc85435) --- dlls/imm32/imm.c | 23 +++++++++++++---------- dlls/imm32/tests/imm32.c | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 31a63ecbc80..b88ca86c15c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2698,25 +2698,28 @@ BOOL WINAPI ImmSetOpenStatus( HIMC himc, BOOL status ) /*********************************************************************** * ImmSetStatusWindowPos (IMM32.@) */ -BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) +BOOL WINAPI ImmSetStatusWindowPos( HIMC himc, POINT *pos ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; - TRACE("(%p, %p)\n", hIMC, lpptPos); + TRACE( "himc %p, pos %s\n", himc, wine_dbgstr_point( pos ) ); - if (!data || !lpptPos) + if (!pos) { - SetLastError(ERROR_INVALID_HANDLE); + SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; - TRACE("\t%s\n", wine_dbgstr_point(lpptPos)); + ctx->ptStatusWndPos = *pos; + ctx->fdwInit |= INIT_STATUSWNDPOS; - data->IMC.ptStatusWndPos = *lpptPos; - ImmNotifyIME( hIMC, NI_CONTEXTUPDATED, 0, IMC_SETSTATUSWINDOWPOS); - imc_notify_ime( data, IMN_SETSTATUSWINDOWPOS, 0 ); + ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETSTATUSWINDOWPOS ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETSTATUSWINDOWPOS, 0 ); + + ImmUnlockIMC( himc ); return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 3aa50ea3582..cc09d5d969d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6188,7 +6188,7 @@ static void test_ImmSetStatusWindowPos(void) .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETSTATUSWINDOWPOS}, }, - {.todo = TRUE}, + {0}, }; INPUTCONTEXT *ctx; POINT pos; @@ -6235,7 +6235,7 @@ static void test_ImmSetStatusWindowPos(void) ctx->fdwInit = 0; ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) ); ok_seq( set_status_window_pos_0_seq ); - todo_wine ok_eq( INIT_STATUSWNDPOS, ctx->fdwInit, UINT, "%u" ); + ok_eq( INIT_STATUSWNDPOS, ctx->fdwInit, UINT, "%u" ); ok_ret( 1, ImmSetStatusWindowPos( himc, &pos ) ); ok_seq( set_status_window_pos_0_seq ); From 6e821175863eeedff5585081a5ed7683a6b9a43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 8 Apr 2023 11:59:38 +0200 Subject: [PATCH 251/758] imm32: Use INPUTCONTEXT directly in ImmGetStatusWindowPos. (cherry picked from commit 9a4b9a3ae5c9b283ca33171afb90fd94ee3bb33b) --- dlls/imm32/imm.c | 16 ++++++++-------- dlls/imm32/tests/imm32.c | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index b88ca86c15c..8ff3a99324a 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2092,18 +2092,18 @@ UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW ) /*********************************************************************** * ImmGetStatusWindowPos (IMM32.@) */ -BOOL WINAPI ImmGetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos) +BOOL WINAPI ImmGetStatusWindowPos( HIMC himc, POINT *pos ) { - struct imc *data = get_imc_data( hIMC ); - - TRACE("(%p, %p)\n", hIMC, lpptPos); + INPUTCONTEXT *ctx; + BOOL ret; - if (!data || !lpptPos) - return FALSE; + TRACE( "himc %p, pos %p\n", himc, pos ); - *lpptPos = data->IMC.ptStatusWndPos; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + if ((ret = !!(ctx->fdwInit & INIT_STATUSWNDPOS))) *pos = ctx->ptStatusWndPos; + ImmUnlockIMC( himc ); - return TRUE; + return ret; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index cc09d5d969d..0dda2217979 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6220,9 +6220,9 @@ static void test_ImmSetStatusWindowPos(void) ctx->ptStatusWndPos.x = 0xdeadbeef; ctx->ptStatusWndPos.y = 0xfeedcafe; ctx->fdwInit = ~INIT_STATUSWNDPOS; - todo_wine ok_ret( 0, ImmGetStatusWindowPos( himc, &pos ) ); - todo_wine ok_eq( 0xcdcdcdcd, pos.x, UINT, "%u" ); - todo_wine ok_eq( 0xcdcdcdcd, pos.y, UINT, "%u" ); + ok_ret( 0, ImmGetStatusWindowPos( himc, &pos ) ); + ok_eq( 0xcdcdcdcd, pos.x, UINT, "%u" ); + ok_eq( 0xcdcdcdcd, pos.y, UINT, "%u" ); ctx->fdwInit = INIT_STATUSWNDPOS; ok_ret( 1, ImmGetStatusWindowPos( himc, &pos ) ); ok_eq( 0xdeadbeef, pos.x, UINT, "%u" ); From f4d64819da57178fe54c035c58af8fe6a0d7ea2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 01:15:56 +0200 Subject: [PATCH 252/758] imm32: Use INPUTCONTEXT directly in ImmSetCompositionFont(A|W). (cherry picked from commit d4318270da18ffb72a214910db1583366d2d0786) --- dlls/imm32/imm.c | 78 +++++++++++++++++++++++++++------------- dlls/imm32/tests/imm32.c | 18 +++++----- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 8ff3a99324a..d261725bc08 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -132,6 +132,12 @@ static BOOL ime_is_unicode( const struct ime *ime ) return !!(ime->info.fdwProperty & IME_PROP_UNICODE); } +static BOOL input_context_is_unicode( INPUTCONTEXT *ctx ) +{ + struct imc *imc = CONTAINING_RECORD( ctx, struct imc, IMC ); + return !imc->ime || ime_is_unicode( imc->ime ); +} + static BOOL IMM_DestroyContext(HIMC hIMC); static struct imc *get_imc_data( HIMC hIMC ); @@ -2438,49 +2444,73 @@ BOOL WINAPI ImmSetCandidateWindow( /*********************************************************************** * ImmSetCompositionFontA (IMM32.@) */ -BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) +BOOL WINAPI ImmSetCompositionFontA( HIMC himc, LOGFONTA *fontA ) { - struct imc *data = get_imc_data( hIMC ); - TRACE("(%p, %p)\n", hIMC, lplf); + INPUTCONTEXT *ctx; + BOOL ret = TRUE; - if (!data || !lplf) + TRACE( "hwnd %p, fontA %p\n", himc, fontA ); + + if (!fontA) return FALSE; + + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + + if (input_context_is_unicode( ctx )) { - SetLastError(ERROR_INVALID_HANDLE); - return FALSE; + LOGFONTW fontW; + memcpy( &fontW, fontA, offsetof(LOGFONTW, lfFaceName) ); + MultiByteToWideChar( CP_ACP, 0, fontA->lfFaceName, -1, fontW.lfFaceName, LF_FACESIZE ); + ret = ImmSetCompositionFontW( himc, &fontW ); } + else + { + ctx->lfFont.A = *fontA; + ctx->fdwInit |= INIT_LOGFONT; - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONFONT, 0 ); + } - memcpy(&data->IMC.lfFont.W,lplf,sizeof(LOGFONTA)); - MultiByteToWideChar(CP_ACP, 0, lplf->lfFaceName, -1, data->IMC.lfFont.W.lfFaceName, - LF_FACESIZE); - ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT); - imc_notify_ime( data, IMN_SETCOMPOSITIONFONT, 0 ); + ImmUnlockIMC( himc ); - return TRUE; + return ret; } /*********************************************************************** * ImmSetCompositionFontW (IMM32.@) */ -BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) +BOOL WINAPI ImmSetCompositionFontW( HIMC himc, LOGFONTW *fontW ) { - struct imc *data = get_imc_data( hIMC ); - TRACE("(%p, %p)\n", hIMC, lplf); + INPUTCONTEXT *ctx; + BOOL ret = TRUE; - if (!data || !lplf) + TRACE( "hwnd %p, fontW %p\n", himc, fontW ); + + if (!fontW) return FALSE; + + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + + if (!input_context_is_unicode( ctx )) { - SetLastError(ERROR_INVALID_HANDLE); - return FALSE; + LOGFONTA fontA; + memcpy( &fontA, fontW, offsetof(LOGFONTA, lfFaceName) ); + WideCharToMultiByte( CP_ACP, 0, fontW->lfFaceName, -1, fontA.lfFaceName, LF_FACESIZE, NULL, NULL ); + ret = ImmSetCompositionFontA( himc, &fontA ); } + else + { + ctx->lfFont.W = *fontW; + ctx->fdwInit |= INIT_LOGFONT; - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONFONT, 0 ); + } - data->IMC.lfFont.W = *lplf; - ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT); - imc_notify_ime( data, IMN_SETCOMPOSITIONFONT, 0 ); + ImmUnlockIMC( himc ); - return TRUE; + return ret; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 0dda2217979..2e59f04e108 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6286,7 +6286,7 @@ static void test_ImmSetCompositionFont( BOOL unicode ) .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCOMPOSITIONFONT}, }, - {.todo = TRUE}, + {0}, }; LOGFONTW fontW, expect_fontW = { @@ -6369,33 +6369,33 @@ static void test_ImmSetCompositionFont( BOOL unicode ) ctx->fdwInit = 0; memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) ); ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); - todo_wine ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" ); + ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" ); ok_seq( set_composition_font_0_seq ); ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); ok_seq( set_composition_font_0_seq ); if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW ); - else check_logfont_a_( __LINE__, &ctx->lfFont.A, &expect_fontA, TRUE ); + else check_logfont_a( &ctx->lfFont.A, &expect_fontA ); ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); - check_logfont_w( &fontW, &expect_fontW ); + check_logfont_w_( __LINE__, &fontW, &expect_fontW, !unicode ); ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); - check_logfont_a( &fontA, &expect_fontA ); + check_logfont_a_( __LINE__, &fontA, &expect_fontA, !unicode ); ctx->hWnd = hwnd; ctx->fdwInit = 0; memset( &ctx->lfFont, 0xcd, sizeof(ctx->lfFont) ); ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) ); - todo_wine ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" ); + ok_eq( INIT_LOGFONT, ctx->fdwInit, UINT, "%u" ); ok_seq( set_composition_font_0_seq ); ok_ret( 1, ImmSetCompositionFontA( himc, &expect_fontA ) ); ok_seq( set_composition_font_0_seq ); if (unicode) check_logfont_w( &ctx->lfFont.W, &expect_fontW ); - else check_logfont_a_( __LINE__, &ctx->lfFont.A, &expect_fontA, TRUE ); + else check_logfont_a( &ctx->lfFont.A, &expect_fontA ); ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); - check_logfont_w( &fontW, &expect_fontW ); + check_logfont_w_( __LINE__, &fontW, &expect_fontW, !unicode ); ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); - check_logfont_a( &fontA, &expect_fontA ); + check_logfont_a_( __LINE__, &fontA, &expect_fontA, !unicode ); ctx->hWnd = 0; ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); From 12faeaa9a762265280b6719b06ed51c195453fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 13 Apr 2023 12:00:18 +0200 Subject: [PATCH 253/758] imm32: Use INPUTCONTEXT directly in ImmGetCompositionFont(A|W). (cherry picked from commit 93b6c4557d442cdd71cb01fa2ea05810306f91ab) --- dlls/imm32/imm.c | 51 ++++++++++++++++++++++++++-------------- dlls/imm32/tests/imm32.c | 28 +++++++++++----------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index d261725bc08..68196bf0149 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1413,38 +1413,53 @@ BOOL WINAPI ImmGetCandidateWindow( /*********************************************************************** * ImmGetCompositionFontA (IMM32.@) */ -BOOL WINAPI ImmGetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf) +BOOL WINAPI ImmGetCompositionFontA( HIMC himc, LOGFONTA *fontA ) { - LOGFONTW lfW; - BOOL rc; + INPUTCONTEXT *ctx; + LOGFONTW fontW; + BOOL ret = TRUE; - TRACE("(%p, %p):\n", hIMC, lplf); + TRACE( "himc %p, fontA %p\n", himc, fontA ); - rc = ImmGetCompositionFontW(hIMC,&lfW); - if (!rc || !lplf) - return FALSE; + if (!fontA) return FALSE; - memcpy(lplf,&lfW,sizeof(LOGFONTA)); - WideCharToMultiByte(CP_ACP, 0, lfW.lfFaceName, -1, lplf->lfFaceName, - LF_FACESIZE, NULL, NULL); - return TRUE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + if (!(ctx->fdwInit & INIT_LOGFONT)) ret = FALSE; + else if (!input_context_is_unicode( ctx )) *fontA = ctx->lfFont.A; + else if ((ret = ImmGetCompositionFontW( himc, &fontW ))) + { + memcpy( fontA, &fontW, offsetof(LOGFONTA, lfFaceName) ); + WideCharToMultiByte( CP_ACP, 0, fontW.lfFaceName, -1, fontA->lfFaceName, LF_FACESIZE, NULL, NULL ); + } + ImmUnlockIMC( himc ); + + return ret; } /*********************************************************************** * ImmGetCompositionFontW (IMM32.@) */ -BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) +BOOL WINAPI ImmGetCompositionFontW( HIMC himc, LOGFONTW *fontW ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; + LOGFONTA fontA; + BOOL ret = TRUE; - TRACE("(%p, %p):\n", hIMC, lplf); + TRACE( "himc %p, fontW %p\n", himc, fontW ); - if (!data || !lplf) - return FALSE; + if (!fontW) return FALSE; - *lplf = data->IMC.lfFont.W; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + if (!(ctx->fdwInit & INIT_LOGFONT)) ret = FALSE; + else if (input_context_is_unicode( ctx )) *fontW = ctx->lfFont.W; + else if ((ret = ImmGetCompositionFontA( himc, &fontA ))) + { + memcpy( fontW, &fontA, offsetof(LOGFONTW, lfFaceName) ); + MultiByteToWideChar( CP_ACP, 0, fontA.lfFaceName, -1, fontW->lfFaceName, LF_FACESIZE ); + } + ImmUnlockIMC( himc ); - return TRUE; + return ret; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 2e59f04e108..3b8b4755080 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -182,8 +182,8 @@ static void check_composition_form_( int line, COMPOSITIONFORM *form, const COMP check_member_rect_( __FILE__, line, *form, *expect, rcArea ); } -#define check_logfont_w( a, b ) check_logfont_w_( __LINE__, a, b, FALSE ) -static void check_logfont_w_( int line, LOGFONTW *font, const LOGFONTW *expect, BOOL todo ) +#define check_logfont_w( a, b ) check_logfont_w_( __LINE__, a, b ) +static void check_logfont_w_( int line, LOGFONTW *font, const LOGFONTW *expect ) { check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight ); check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth ); @@ -198,11 +198,11 @@ static void check_logfont_w_( int line, LOGFONTW *font, const LOGFONTW *expect, check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision ); check_member_( __FILE__, line, *font, *expect, "%u", lfQuality ); check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily ); - todo_wine_if(todo) check_member_wstr_( __FILE__, line, *font, *expect, lfFaceName ); + check_member_wstr_( __FILE__, line, *font, *expect, lfFaceName ); } -#define check_logfont_a( a, b ) check_logfont_a_( __LINE__, a, b, FALSE ) -static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect, BOOL todo ) +#define check_logfont_a( a, b ) check_logfont_a_( __LINE__, a, b ) +static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect ) { check_member_( __FILE__, line, *font, *expect, "%lu", lfHeight ); check_member_( __FILE__, line, *font, *expect, "%lu", lfWidth ); @@ -217,7 +217,7 @@ static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect, check_member_( __FILE__, line, *font, *expect, "%u", lfClipPrecision ); check_member_( __FILE__, line, *font, *expect, "%u", lfQuality ); check_member_( __FILE__, line, *font, *expect, "%u", lfPitchAndFamily ); - todo_wine_if(todo) check_member_str_( __FILE__, line, *font, *expect, lfFaceName ); + check_member_str_( __FILE__, line, *font, *expect, lfFaceName ); } #define DEFINE_EXPECT(func) \ @@ -6357,13 +6357,13 @@ static void test_ImmSetCompositionFont( BOOL unicode ) if (unicode) ctx->lfFont.W = expect_fontW; else ctx->lfFont.A = expect_fontA; ctx->fdwInit = ~INIT_LOGFONT; - todo_wine ok_ret( 0, ImmGetCompositionFontW( himc, &fontW ) ); - todo_wine ok_ret( 0, ImmGetCompositionFontA( himc, &fontA ) ); + ok_ret( 0, ImmGetCompositionFontW( himc, &fontW ) ); + ok_ret( 0, ImmGetCompositionFontA( himc, &fontA ) ); ctx->fdwInit = INIT_LOGFONT; ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); - check_logfont_w_( __LINE__, &fontW, &expect_fontW, !unicode ); + check_logfont_w( &fontW, &expect_fontW ); ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); - check_logfont_a_( __LINE__, &fontA, &expect_fontA, !unicode ); + check_logfont_a( &fontA, &expect_fontA ); ctx->hWnd = hwnd; ctx->fdwInit = 0; @@ -6377,9 +6377,9 @@ static void test_ImmSetCompositionFont( BOOL unicode ) else check_logfont_a( &ctx->lfFont.A, &expect_fontA ); ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); - check_logfont_w_( __LINE__, &fontW, &expect_fontW, !unicode ); + check_logfont_w( &fontW, &expect_fontW ); ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); - check_logfont_a_( __LINE__, &fontA, &expect_fontA, !unicode ); + check_logfont_a( &fontA, &expect_fontA ); ctx->hWnd = hwnd; ctx->fdwInit = 0; @@ -6393,9 +6393,9 @@ static void test_ImmSetCompositionFont( BOOL unicode ) else check_logfont_a( &ctx->lfFont.A, &expect_fontA ); ok_ret( 1, ImmGetCompositionFontW( himc, &fontW ) ); - check_logfont_w_( __LINE__, &fontW, &expect_fontW, !unicode ); + check_logfont_w( &fontW, &expect_fontW ); ok_ret( 1, ImmGetCompositionFontA( himc, &fontA ) ); - check_logfont_a_( __LINE__, &fontA, &expect_fontA, !unicode ); + check_logfont_a( &fontA, &expect_fontA ); ctx->hWnd = 0; ok_ret( 1, ImmSetCompositionFontW( himc, &expect_fontW ) ); From 946de3f8c263c2247bc5f79dca1fe8a85dbab2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Apr 2023 01:11:14 +0200 Subject: [PATCH 254/758] imm32: Use INPUTCONTEXT directly in ImmSetCandidateWindow. (cherry picked from commit cf03ab413383df151d82a573d5934e18c0889360) --- dlls/imm32/imm.c | 42 ++++++++++++++++++---------------------- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 68196bf0149..aa63be82e4b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -127,6 +127,14 @@ static const char *debugstr_composition( const COMPOSITIONFORM *composition ) wine_dbgstr_rect( &composition->rcArea ) ); } +static const char *debugstr_candidate( const CANDIDATEFORM *candidate ) +{ + if (!candidate) return "(null)"; + return wine_dbg_sprintf( "idx %#lx, style %#lx, pos %s, area %s", candidate->dwIndex, + candidate->dwStyle, wine_dbgstr_point( &candidate->ptCurrentPos ), + wine_dbgstr_rect( &candidate->rcArea ) ); +} + static BOOL ime_is_unicode( const struct ime *ime ) { return !!(ime->info.fdwProperty & IME_PROP_UNICODE); @@ -827,13 +835,6 @@ static void imc_send_message( struct imc *imc, TRANSMSG *message ) SendMessageW( target, message->message, message->wParam, message->lParam ); } -static LRESULT imc_notify_ime( struct imc *imc, WPARAM notify, LPARAM lparam ) -{ - HWND target; - if (!(target = imc->IMC.hWnd) && !(target = GetFocus())) return 0; - return SendMessageW( target, WM_IME_NOTIFY, notify, lparam ); -} - /*********************************************************************** * ImmSetActiveContext (IMM32.@) */ @@ -2429,29 +2430,24 @@ LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam) /*********************************************************************** * ImmSetCandidateWindow (IMM32.@) */ -BOOL WINAPI ImmSetCandidateWindow( - HIMC hIMC, LPCANDIDATEFORM lpCandidate) +BOOL WINAPI ImmSetCandidateWindow( HIMC himc, CANDIDATEFORM *candidate ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; - TRACE("(%p, %p)\n", hIMC, lpCandidate); + TRACE( "hwnd %p, candidate %s\n", himc, debugstr_candidate( candidate ) ); - if (!data || !lpCandidate) - return FALSE; + if (!candidate) return FALSE; + if (candidate->dwIndex >= ARRAY_SIZE(ctx->cfCandForm)) return FALSE; - if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; - TRACE("\t%lx, %lx, %s, %s\n", - lpCandidate->dwIndex, lpCandidate->dwStyle, - wine_dbgstr_point(&lpCandidate->ptCurrentPos), - wine_dbgstr_rect(&lpCandidate->rcArea)); + ctx->cfCandForm[candidate->dwIndex] = *candidate; - if (lpCandidate->dwIndex >= ARRAY_SIZE(data->IMC.cfCandForm)) - return FALSE; + ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS ); + SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCANDIDATEPOS, 1 << candidate->dwIndex ); - data->IMC.cfCandForm[lpCandidate->dwIndex] = *lpCandidate; - ImmNotifyIME(hIMC, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS); - imc_notify_ime( data, IMN_SETCANDIDATEPOS, 1 << lpCandidate->dwIndex ); + ImmUnlockIMC( himc ); return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 3b8b4755080..cf6c521e52f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6442,7 +6442,7 @@ static void test_ImmSetCandidateWindow(void) .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_NOTIFY, .notify = {.action = NI_CONTEXTUPDATED, .index = 0, .value = IMC_SETCANDIDATEPOS}, }, - {.todo = TRUE}, + {0}, }; CANDIDATEFORM cand_form, expect_form = { From 44ab9a643023e489c0fe643de5dc4ec26d57aaa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 8 Apr 2023 12:02:21 +0200 Subject: [PATCH 255/758] imm32: Use INPUTCONTEXT directly in ImmGetCandidateWindow. (cherry picked from commit 01677af42ae683d3b86e68e3a7cc71b1d68844ed) --- dlls/imm32/imm.c | 24 ++++++++++-------------- dlls/imm32/tests/imm32.c | 14 +++++++------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index aa63be82e4b..62b95eba77a 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1390,25 +1390,21 @@ DWORD WINAPI ImmGetCandidateListW( /*********************************************************************** * ImmGetCandidateWindow (IMM32.@) */ -BOOL WINAPI ImmGetCandidateWindow( - HIMC hIMC, DWORD dwIndex, LPCANDIDATEFORM lpCandidate) +BOOL WINAPI ImmGetCandidateWindow( HIMC himc, DWORD index, CANDIDATEFORM *candidate ) { - struct imc *data = get_imc_data( hIMC ); - - TRACE("%p, %ld, %p\n", hIMC, dwIndex, lpCandidate); - - if (!data || !lpCandidate) - return FALSE; + INPUTCONTEXT *ctx; + BOOL ret = TRUE; - if (dwIndex >= ARRAY_SIZE(data->IMC.cfCandForm)) - return FALSE; + TRACE( "himc %p, index %lu, candidate %p\n", himc, index, candidate ); - if (data->IMC.cfCandForm[dwIndex].dwIndex != dwIndex) - return FALSE; + if (!candidate) return FALSE; - *lpCandidate = data->IMC.cfCandForm[dwIndex]; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + if (ctx->cfCandForm[index].dwIndex == -1) ret = FALSE; + else *candidate = ctx->cfCandForm[index]; + ImmUnlockIMC( himc ); - return TRUE; + return ret; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index cf6c521e52f..a623cabef7b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -5653,21 +5653,21 @@ static void test_ImmGetCandidateWindow(void) ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" ); ok_ret( 0, ImmGetCandidateWindow( default_himc, 3, &cand_form ) ); ok_eq( 0xcdcdcdcd, cand_form.dwIndex, UINT, "%u" ); - todo_wine ok_ret( 1, ImmGetCandidateWindow( default_himc, 4, &cand_form ) ); + ok_ret( 1, ImmGetCandidateWindow( default_himc, 4, &cand_form ) ); ok_seq( empty_sequence ); ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) ); ok_seq( empty_sequence ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); ctx->cfCandForm[0] = expect_form; - todo_wine ok_ret( 1, ImmGetCandidateWindow( himc, 0, &cand_form ) ); - todo_wine check_candidate_form( &cand_form, &expect_form ); + ok_ret( 1, ImmGetCandidateWindow( himc, 0, &cand_form ) ); + check_candidate_form( &cand_form, &expect_form ); ok_seq( empty_sequence ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); ok( !!ctx, "ImmLockIMC failed, error %lu\n", GetLastError() ); ctx->cfCandForm[0].dwIndex = -1; @@ -6481,8 +6481,8 @@ static void test_ImmSetCandidateWindow(void) memset( &cand_form, 0xcd, sizeof(cand_form) ); ok_ret( 0, ImmGetCandidateWindow( himc, 0, &cand_form ) ); ok_eq( 0xcdcdcdcd, cand_form.dwStyle, UINT, "%#x" ); - todo_wine ok_ret( 1, ImmGetCandidateWindow( himc, 1, &cand_form ) ); - todo_wine check_candidate_form( &cand_form, &expect_form ); + ok_ret( 1, ImmGetCandidateWindow( himc, 1, &cand_form ) ); + check_candidate_form( &cand_form, &expect_form ); ok_ret( 1, ImmGetCandidateWindow( himc, 2, &cand_form ) ); check_candidate_form( &cand_form, &expect_form ); ok_seq( empty_sequence ); From 8fe3abb12c3b1ca8faaca67287a3c2076aa656e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 31 Mar 2023 12:01:32 +0200 Subject: [PATCH 256/758] imm32/tests: Test that ImmSetOpenStatus doesn't set IMMGWL_IMC. (cherry picked from commit 3e3706adccf45757593c2c5206bd4ffeb9801077) --- dlls/imm32/tests/imm32.c | 92 ++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index a623cabef7b..a062604f116 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4238,6 +4238,37 @@ static void test_ImmUnregisterWord( BOOL unicode ) winetest_pop_context(); } +struct ime_windows +{ + HWND ime_hwnd; + HWND ime_ui_hwnd; +}; + +static BOOL CALLBACK enum_thread_ime_windows( HWND hwnd, LPARAM lparam ) +{ + struct ime_windows *params = (void *)lparam; + WCHAR buffer[256]; + UINT ret; + + ime_trace( "hwnd %p, lparam %#Ix\n", hwnd, lparam ); + + ret = RealGetWindowClassW( hwnd, buffer, ARRAY_SIZE(buffer) ); + ok( ret, "RealGetWindowClassW returned %#x\n", ret ); + + if (!wcscmp( buffer, L"IME" )) + { + ok( !params->ime_hwnd, "Found extra IME window %p\n", hwnd ); + params->ime_hwnd = hwnd; + } + if (!wcscmp( buffer, ime_ui_class.lpszClassName )) + { + ok( !params->ime_ui_hwnd, "Found extra IME UI window %p\n", hwnd ); + params->ime_ui_hwnd = hwnd; + } + + return TRUE; +} + static void test_ImmSetConversionStatus(void) { const struct ime_call set_conversion_status_0_seq[] = @@ -4473,8 +4504,10 @@ static void test_ImmSetOpenStatus(void) {0}, }; HKL hkl; + struct ime_windows ime_windows = {0}; DWORD old_status, status; INPUTCONTEXT *ctx; + HIMC himc; ok_ret( 0, ImmGetOpenStatus( 0 ) ); old_status = ImmGetOpenStatus( default_himc ); @@ -4521,6 +4554,22 @@ static void test_ImmSetOpenStatus(void) ok_eq( 0xdeadbeef, status, UINT, "%#x" ); ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); + ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" ); + ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" ); + ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) ); + todo_wine ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) ); + todo_wine ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_ret( 1, ImmDestroyContext( himc ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) ); ok_seq( empty_sequence ); @@ -4640,37 +4689,6 @@ static void test_ImmProcessKey(void) ok_ret( 1, DestroyWindow( hwnd ) ); } -struct ime_windows -{ - HWND ime_hwnd; - HWND ime_ui_hwnd; -}; - -static BOOL CALLBACK enum_thread_ime_windows( HWND hwnd, LPARAM lparam ) -{ - struct ime_windows *params = (void *)lparam; - WCHAR buffer[256]; - UINT ret; - - ime_trace( "hwnd %p, lparam %#Ix\n", hwnd, lparam ); - - ret = RealGetWindowClassW( hwnd, buffer, ARRAY_SIZE(buffer) ); - ok( ret, "RealGetWindowClassW returned %#x\n", ret ); - - if (!wcscmp( buffer, L"IME" )) - { - ok( !params->ime_hwnd, "Found extra IME window %p\n", hwnd ); - params->ime_hwnd = hwnd; - } - if (!wcscmp( buffer, ime_ui_class.lpszClassName )) - { - ok( !params->ime_ui_hwnd, "Found extra IME UI window %p\n", hwnd ); - params->ime_ui_hwnd = hwnd; - } - - return TRUE; -} - static void test_ImmActivateLayout(void) { const struct ime_call activate_seq[] = @@ -5231,6 +5249,7 @@ static void test_ImmSetActiveContext(void) {0}, }; HKL hkl; + struct ime_windows ime_windows = {0}; HIMC himc; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -5247,6 +5266,10 @@ static void test_ImmSetActiveContext(void) memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; + ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); + ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" ); + ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" ); + SetLastError( 0xdeadbeef ); ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) ); ok_seq( activate_0_seq ); @@ -5265,6 +5288,13 @@ static void test_ImmSetActiveContext(void) ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); activate_1_seq[0].himc = himc; ok_seq( activate_1_seq ); + + ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); + ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); + todo_wine ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_ret( 1, ImmDestroyContext( himc ) ); ok_ret( 1, ImmActivateLayout( default_hkl ) ); From 41d6842f686f914e1cd4725731f7e22d2ed75343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 17 Apr 2023 09:17:31 +0200 Subject: [PATCH 257/758] imm32/tests: Check IME UI visibility vs ImmSetCompositionWindow. (cherry picked from commit c8d560377692132740a6fb404f8a5d23da43eb0b) --- dlls/imm32/tests/imm32.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index a062604f116..5542887c168 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -2907,6 +2907,8 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected ime_call_count = 0; } +static BOOL check_WM_SHOWWINDOW; + static BOOL ignore_message( UINT msg ) { switch (msg) @@ -2925,6 +2927,8 @@ static BOOL ignore_message( UINT msg ) case WM_IME_KEYDOWN: case WM_IME_KEYUP: return FALSE; + case WM_SHOWWINDOW: + return !check_WM_SHOWWINDOW; default: return TRUE; } @@ -5269,11 +5273,13 @@ static void test_ImmSetActiveContext(void) ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" ); ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" ); + ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); SetLastError( 0xdeadbeef ); ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) ); ok_seq( activate_0_seq ); ok_ret( 0, GetLastError() ); + ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, TRUE ) ); ok_seq( activate_0_seq ); ok_ret( 1, ImmSetActiveContext( hwnd, default_himc, FALSE ) ); @@ -5291,9 +5297,11 @@ static void test_ImmSetActiveContext(void) ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); + ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); todo_wine ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); ok_ret( 1, ImmDestroyContext( himc ) ); @@ -6124,6 +6132,7 @@ static void test_ImmSetCompositionWindow(void) .ptCurrentPos = {.x = 123, .y = 456}, .rcArea = {.left = 1, .top = 2, .right = 3, .bottom = 4}, }; + struct ime_windows ime_windows = {0}; INPUTCONTEXT *ctx; HIMC himc; HKL hkl; @@ -6149,6 +6158,10 @@ static void test_ImmSetCompositionWindow(void) set_composition_window_0_seq[0].himc = himc; set_composition_window_1_seq[0].himc = himc; + ok_ret( 1, EnumThreadWindows( GetCurrentThreadId(), enum_thread_ime_windows, (LPARAM)&ime_windows ) ); + ok_ne( NULL, ime_windows.ime_hwnd, HWND, "%p" ); + ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" ); + ctx->cfCompForm = expect_form; ctx->fdwInit = ~INIT_COMPFORM; memset( &comp_form, 0xcd, sizeof(comp_form) ); @@ -6158,14 +6171,27 @@ static void test_ImmSetCompositionWindow(void) ok_ret( 1, ImmGetCompositionWindow( himc, &comp_form ) ); check_composition_form( &comp_form, &expect_form ); ok_seq( empty_sequence ); + ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); + + ok_ret( 0, ShowWindow( ime_windows.ime_ui_hwnd, SW_SHOWNOACTIVATE ) ); + process_messages(); + ok_seq( empty_sequence ); + check_WM_SHOWWINDOW = TRUE; ctx->hWnd = hwnd; ctx->fdwInit = 0; memset( &comp_form, 0xcd, sizeof(comp_form) ); ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) ); - ok_seq( set_composition_window_0_seq ); + process_messages(); + todo_wine ok_seq( set_composition_window_0_seq ); ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" ); check_composition_form( &ctx->cfCompForm, &comp_form ); + ok_ret( 1, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); + check_WM_SHOWWINDOW = FALSE; + + ShowWindow( ime_windows.ime_ui_hwnd, SW_HIDE ); + process_messages(); + ok_seq( empty_sequence ); ok_ret( 1, ImmSetCompositionWindow( himc, &expect_form ) ); ok_seq( set_composition_window_0_seq ); From 3129b5f2de00f8fda2705afa1a9199b4a4aed5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 17 Apr 2023 09:47:01 +0200 Subject: [PATCH 258/758] imm32/tests: Check ImmSetActiveContext effect on INPUTCONTEXT hWnd member. (cherry picked from commit 53ae92fab44ca632c71300d1ad3b0cd766f0fd57) --- dlls/imm32/tests/imm32.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 5542887c168..6d5a751b837 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -5240,6 +5240,18 @@ static void test_ImmSetActiveContext(void) }, {0}, }; + struct ime_call deactivate_2_seq[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, + .func = IME_SET_ACTIVE_CONTEXT, .set_active_context = {.flag = 0} + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_SETCONTEXT, .wparam = 0, .lparam = ISC_SHOWUIALL} + }, + {0}, + }; struct ime_call activate_1_seq[] = { { @@ -5254,6 +5266,7 @@ static void test_ImmSetActiveContext(void) }; HKL hkl; struct ime_windows ime_windows = {0}; + INPUTCONTEXT *ctx; HIMC himc; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -5287,6 +5300,10 @@ static void test_ImmSetActiveContext(void) himc = ImmCreateContext(); ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + ok_eq( 0, ctx->hWnd, HWND, "%p" ); + ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) ); deactivate_1_seq[3].himc = himc; deactivate_1_seq[4].himc = himc; @@ -5295,14 +5312,35 @@ static void test_ImmSetActiveContext(void) activate_1_seq[0].himc = himc; ok_seq( activate_1_seq ); + ctx->hWnd = (HWND)0xdeadbeef; + ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) ); + todo_wine ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" ); + deactivate_2_seq[0].himc = himc; + ok_seq( deactivate_2_seq ); + + ctx->hWnd = 0; + ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); + ok_eq( hwnd, ctx->hWnd, HWND, "%p" ); + activate_1_seq[0].himc = himc; + ok_seq( activate_1_seq ); + ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 1, ImmSetActiveContext( hwnd, himc, TRUE ) ); ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + + ctx->hWnd = 0; ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); todo_wine ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); + ok_eq( hwnd, ctx->hWnd, HWND, "%p" ); + + ctx->hWnd = (HWND)0xdeadbeef; + ok_eq( himc, ImmGetContext( hwnd ), HIMC, "%p" ); + todo_wine ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" ); + ok_ret( 1, ImmReleaseContext( hwnd, himc ) ); + ok_ret( 1, ImmUnlockIMC( himc ) ); ok_ret( 1, ImmDestroyContext( himc ) ); ok_ret( 1, ImmActivateLayout( default_hkl ) ); From 52082635ff0901f75d5aa090c4a5a6509dead190 Mon Sep 17 00:00:00 2001 From: Byeongsik Jeon Date: Mon, 17 Apr 2023 06:28:41 +0900 Subject: [PATCH 259/758] imm32: Stop updating INPUTCONTEXT hWnd member in ImmGetContext. (cherry picked from commit 5ef8554ee9e607def521a07eafa390ad321bfccb) --- dlls/imm32/imm.c | 3 +-- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 62b95eba77a..0bd71244203 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1787,8 +1787,7 @@ HIMC WINAPI ImmGetContext(HWND hWnd) if (rc) { struct imc *data = get_imc_data( rc ); - if (data) data->IMC.hWnd = hWnd; - else rc = 0; + if (!data) rc = 0; } TRACE("returning %p\n", rc); diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 6d5a751b837..d9f8c7c446a 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -5337,7 +5337,7 @@ static void test_ImmSetActiveContext(void) ctx->hWnd = (HWND)0xdeadbeef; ok_eq( himc, ImmGetContext( hwnd ), HIMC, "%p" ); - todo_wine ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" ); + ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" ); ok_ret( 1, ImmReleaseContext( hwnd, himc ) ); ok_ret( 1, ImmUnlockIMC( himc ) ); From bb8462482c2264133bbd73f77cfc5d49e9945b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 17 Apr 2023 10:08:58 +0200 Subject: [PATCH 260/758] imm32: Forward ImmGetContext to NtUserGetWindowInputContext directly. (cherry picked from commit 422ee56c1b1f489d3a7a7aab97950ee93cc91549) --- dlls/imm32/imm.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 0bd71244203..5a32138561c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1776,23 +1776,10 @@ BOOL WINAPI ImmGetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) * ImmGetContext (IMM32.@) * */ -HIMC WINAPI ImmGetContext(HWND hWnd) +HIMC WINAPI ImmGetContext( HWND hwnd ) { - HIMC rc; - - TRACE("%p\n", hWnd); - - rc = NtUserGetWindowInputContext(hWnd); - - if (rc) - { - struct imc *data = get_imc_data( rc ); - if (!data) rc = 0; - } - - TRACE("returning %p\n", rc); - - return rc; + TRACE( "hwnd %p\n", hwnd ); + return NtUserGetWindowInputContext( hwnd ); } /*********************************************************************** From 29d3e57b205d57114e6314bd71d60f05603b6072 Mon Sep 17 00:00:00 2001 From: Byeong-Sik Jeon Date: Mon, 17 Apr 2023 10:10:28 +0200 Subject: [PATCH 261/758] imm32: Avoid updating INPUTCONTEXT hWnd on ImmSetActiveContext deactivation. (cherry picked from commit 93e5d7b317e9a9dc90a95dc731e5e07e8b1ec776) --- dlls/imm32/imm.c | 2 +- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 5a32138561c..2fd247f36c7 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -852,7 +852,7 @@ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) if (data) { - data->IMC.hWnd = activate ? hwnd : NULL; + if (activate) data->IMC.hWnd = hwnd; if ((ime = imc_select_ime( data ))) ime->pImeSetActiveContext( himc, activate ); } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index d9f8c7c446a..92b59b7d005 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -5314,7 +5314,7 @@ static void test_ImmSetActiveContext(void) ctx->hWnd = (HWND)0xdeadbeef; ok_ret( 1, ImmSetActiveContext( hwnd, himc, FALSE ) ); - todo_wine ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" ); + ok_eq( (HWND)0xdeadbeef, ctx->hWnd, HWND, "%p" ); deactivate_2_seq[0].himc = himc; ok_seq( deactivate_2_seq ); From 97bf85b08c43bbcd99bea2e806ce7ecbb1332b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 17 Apr 2023 09:35:04 +0200 Subject: [PATCH 262/758] user32: Move WM_IME_COMPOSITION DefWindowProc handlers in separate helpers. (cherry picked from commit 45b096abb489f0a92b03a73328de7ca9958a4322) --- dlls/user32/defwnd.c | 110 +++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c index b4b42b86ae1..6c45f89e392 100644 --- a/dlls/user32/defwnd.c +++ b/dlls/user32/defwnd.c @@ -25,6 +25,59 @@ WINE_DEFAULT_DEBUG_CHANNEL(win); +static void default_ime_compositionW( HWND hwnd, WPARAM wparam, LPARAM lparam ) +{ + WCHAR *buf = NULL; + LONG size, i; + HIMC himc; + + if (!(lparam & GCS_RESULTSTR) || !(himc = ImmGetContext( hwnd ))) return; + + if ((size = ImmGetCompositionStringW( himc, GCS_RESULTSTR, NULL, 0 ))) + { + if (!(buf = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) size = 0; + else size = ImmGetCompositionStringW( himc, GCS_RESULTSTR, buf, size * sizeof(WCHAR) ); + } + ImmReleaseContext( hwnd, himc ); + + for (i = 0; i < size / sizeof(WCHAR); i++) + SendMessageW( hwnd, WM_IME_CHAR, buf[i], 1 ); + HeapFree( GetProcessHeap(), 0, buf ); +} + +static void default_ime_compositionA( HWND hwnd, WPARAM wparam, LPARAM lparam ) +{ + unsigned char lead = 0; + char *buf = NULL; + LONG size, i; + HIMC himc; + + if (!(lparam & GCS_RESULTSTR) || !(himc = ImmGetContext( hwnd ))) return; + + if ((size = ImmGetCompositionStringA( himc, GCS_RESULTSTR, NULL, 0 ))) + { + if (!(buf = HeapAlloc( GetProcessHeap(), 0, size ))) size = 0; + else size = ImmGetCompositionStringA( himc, GCS_RESULTSTR, buf, size ); + } + ImmReleaseContext( hwnd, himc ); + + for (i = 0; i < size; i++) + { + unsigned char c = buf[i]; + if (!lead) + { + if (IsDBCSLeadByte( c )) lead = c; + else SendMessageA( hwnd, WM_IME_CHAR, c, 1 ); + } + else + { + SendMessageA( hwnd, WM_IME_CHAR, MAKEWORD(c, lead), 1 ); + lead = 0; + } + } + + HeapFree( GetProcessHeap(), 0, buf ); +} /*********************************************************************** * DefWindowProcA (USER32.@) @@ -66,41 +119,7 @@ LRESULT WINAPI DefWindowProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam break; case WM_IME_COMPOSITION: - if (lParam & GCS_RESULTSTR) - { - LONG size, i; - unsigned char lead = 0; - char *buf = NULL; - HIMC himc = ImmGetContext( hwnd ); - - if (himc) - { - if ((size = ImmGetCompositionStringA( himc, GCS_RESULTSTR, NULL, 0 ))) - { - if (!(buf = HeapAlloc( GetProcessHeap(), 0, size ))) size = 0; - else size = ImmGetCompositionStringA( himc, GCS_RESULTSTR, buf, size ); - } - ImmReleaseContext( hwnd, himc ); - - for (i = 0; i < size; i++) - { - unsigned char c = buf[i]; - if (!lead) - { - if (IsDBCSLeadByte( c )) - lead = c; - else - SendMessageA( hwnd, WM_IME_CHAR, c, 1 ); - } - else - { - SendMessageA( hwnd, WM_IME_CHAR, MAKEWORD(c, lead), 1 ); - lead = 0; - } - } - HeapFree( GetProcessHeap(), 0, buf ); - } - } + default_ime_compositionA( hwnd, wParam, lParam ); /* fall through */ default: @@ -159,26 +178,7 @@ LRESULT WINAPI DefWindowProcW( break; case WM_IME_COMPOSITION: - if (lParam & GCS_RESULTSTR) - { - LONG size, i; - WCHAR *buf = NULL; - HIMC himc = ImmGetContext( hwnd ); - - if (himc) - { - if ((size = ImmGetCompositionStringW( himc, GCS_RESULTSTR, NULL, 0 ))) - { - if (!(buf = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) size = 0; - else size = ImmGetCompositionStringW( himc, GCS_RESULTSTR, buf, size * sizeof(WCHAR) ); - } - ImmReleaseContext( hwnd, himc ); - - for (i = 0; i < size / sizeof(WCHAR); i++) - SendMessageW( hwnd, WM_IME_CHAR, buf[i], 1 ); - HeapFree( GetProcessHeap(), 0, buf ); - } - } + default_ime_compositionW( hwnd, wParam, lParam ); /* fall through */ default: From e1a2e88ae23632e4c2153bf36f5c943bb21b1e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 17 Apr 2023 09:37:58 +0200 Subject: [PATCH 263/758] user32: Ignore WM_IME_COMPOSITION from the IME UI window in DefWindowProc. As in 6fd3bd9b62f405a54db29dc5a72805063a6099ca. (cherry picked from commit 6fb47669476a2cdd982b5e3a461d0d97ebf74dec) --- dlls/user32/defwnd.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c index 6c45f89e392..60afec4e7c6 100644 --- a/dlls/user32/defwnd.c +++ b/dlls/user32/defwnd.c @@ -119,8 +119,13 @@ LRESULT WINAPI DefWindowProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam break; case WM_IME_COMPOSITION: + { + HWND ime_hwnd = NtUserGetDefaultImeWindow( hwnd ); + if (!ime_hwnd || ime_hwnd == NtUserGetParent( hwnd )) break; + default_ime_compositionA( hwnd, wParam, lParam ); /* fall through */ + } default: result = NtUserMessageCall( hwnd, msg, wParam, lParam, 0, NtUserDefWindowProc, TRUE ); @@ -178,8 +183,13 @@ LRESULT WINAPI DefWindowProcW( break; case WM_IME_COMPOSITION: + { + HWND ime_hwnd = NtUserGetDefaultImeWindow( hwnd ); + if (!ime_hwnd || ime_hwnd == NtUserGetParent( hwnd )) break; + default_ime_compositionW( hwnd, wParam, lParam ); /* fall through */ + } default: result = NtUserMessageCall( hwnd, msg, wParam, lParam, 0, NtUserDefWindowProc, FALSE ); From 63c5f4c143376a94dc8a6e9f0b34801b715d7a38 Mon Sep 17 00:00:00 2001 From: Michael Stefaniuc Date: Wed, 19 Apr 2023 19:28:53 +0200 Subject: [PATCH 264/758] win32u: Use ARRAY_SIZE() instead of open coding it. (cherry picked from commit 5cae9680f44eee6a97cb76fdf5c56d35d5b2ee14) --- dlls/win32u/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 289c8c2fde4..24321d6d5a2 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -398,7 +398,7 @@ static const KBDTABLES kbdus_tables = .pKeyNames = (VSC_LPWSTR *)key_names, .pKeyNamesExt = (VSC_LPWSTR *)key_names_ext, .pusVSCtoVK = (USHORT *)vsc_to_vk, - .bMaxVSCtoVK = sizeof(vsc_to_vk) / sizeof(vsc_to_vk[0]), + .bMaxVSCtoVK = ARRAY_SIZE(vsc_to_vk), .pVSCtoVK_E0 = (VSC_VK *)vsc_to_vk_e0, .pVSCtoVK_E1 = (VSC_VK *)vsc_to_vk_e1, .fLocaleFlags = MAKELONG(0, KBD_VERSION), From 62762abc9166244d47c7d5f9e79f532fbd547c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 14 Apr 2023 09:17:17 +0200 Subject: [PATCH 265/758] imm32: Don't hide/show IME UI window in ImmSetCompositionWindow. (cherry picked from commit 2fda6abfc4ecd1ddbac658fd59b3a4d85d516a2d) --- dlls/imm32/imm.c | 10 ---------- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 2fd247f36c7..554cc00b457 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2629,9 +2629,7 @@ BOOL WINAPI ImmSetCompositionStringW( */ BOOL WINAPI ImmSetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) { - BOOL reshow = FALSE; INPUTCONTEXT *ctx; - HWND ui_hwnd; TRACE( "himc %p, composition %s\n", himc, debugstr_composition( composition ) ); @@ -2641,14 +2639,6 @@ BOOL WINAPI ImmSetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) ctx->cfCompForm = *composition; ctx->fdwInit |= INIT_COMPFORM; - if ((ui_hwnd = get_ime_ui_window()) && IsWindowVisible( ui_hwnd )) - { - reshow = TRUE; - ShowWindow( ui_hwnd, SW_HIDE ); - } - - if (ui_hwnd && reshow) ShowWindow( ui_hwnd, SW_SHOWNOACTIVATE ); - ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONWINDOW ); SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONWINDOW, 0 ); diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 92b59b7d005..9857583fafa 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6221,7 +6221,7 @@ static void test_ImmSetCompositionWindow(void) memset( &comp_form, 0xcd, sizeof(comp_form) ); ok_ret( 1, ImmSetCompositionWindow( himc, &comp_form ) ); process_messages(); - todo_wine ok_seq( set_composition_window_0_seq ); + ok_seq( set_composition_window_0_seq ); ok_eq( INIT_COMPFORM, ctx->fdwInit, UINT, "%u" ); check_composition_form( &ctx->cfCompForm, &comp_form ); ok_ret( 1, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); From 96e1219a4ace49cccbda4fc25768dee9408336aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 14 Apr 2023 09:32:30 +0200 Subject: [PATCH 266/758] imm32: Move ImmAssociateContext(Ex) around. (cherry picked from commit b5c30f8ef26b1ae45ea401a63f4534cb59604d1a) --- dlls/imm32/imm.c | 116 +++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 554cc00b457..f7304897f15 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -865,66 +865,6 @@ BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate) return TRUE; } -/*********************************************************************** - * ImmAssociateContext (IMM32.@) - */ -HIMC WINAPI ImmAssociateContext(HWND hwnd, HIMC imc) -{ - HIMC old; - UINT ret; - - TRACE("(%p, %p):\n", hwnd, imc); - - old = NtUserGetWindowInputContext(hwnd); - ret = NtUserAssociateInputContext(hwnd, imc, 0); - if (ret == AICR_FOCUS_CHANGED) - { - ImmSetActiveContext(hwnd, old, FALSE); - ImmSetActiveContext(hwnd, imc, TRUE); - } - return ret == AICR_FAILED ? 0 : old; -} - - -/* - * Helper function for ImmAssociateContextEx - */ -static BOOL CALLBACK _ImmAssociateContextExEnumProc(HWND hwnd, LPARAM lParam) -{ - HIMC hImc = (HIMC)lParam; - ImmAssociateContext(hwnd,hImc); - return TRUE; -} - -/*********************************************************************** - * ImmAssociateContextEx (IMM32.@) - */ -BOOL WINAPI ImmAssociateContextEx(HWND hwnd, HIMC imc, DWORD flags) -{ - HIMC old; - UINT ret; - - TRACE("(%p, %p, 0x%lx):\n", hwnd, imc, flags); - - if (!hwnd) - return FALSE; - - if (flags == IACE_CHILDREN) - { - EnumChildWindows(hwnd, _ImmAssociateContextExEnumProc, (LPARAM)imc); - return TRUE; - } - - old = NtUserGetWindowInputContext(hwnd); - ret = NtUserAssociateInputContext(hwnd, imc, flags); - if (ret == AICR_FOCUS_CHANGED) - { - ImmSetActiveContext(hwnd, old, FALSE); - ImmSetActiveContext(hwnd, imc, TRUE); - } - return ret != AICR_FAILED; -} - /*********************************************************************** * ImmConfigureIMEA (IMM32.@) */ @@ -1065,6 +1005,62 @@ BOOL WINAPI ImmDestroyContext(HIMC hIMC) return IMM_DestroyContext(hIMC); } +/*********************************************************************** + * ImmAssociateContext (IMM32.@) + */ +HIMC WINAPI ImmAssociateContext( HWND hwnd, HIMC new_himc ) +{ + HIMC old_himc; + UINT ret; + + TRACE( "hwnd %p, new_himc %p\n", hwnd, new_himc ); + + old_himc = NtUserGetWindowInputContext( hwnd ); + ret = NtUserAssociateInputContext( hwnd, new_himc, 0 ); + if (ret == AICR_FOCUS_CHANGED) + { + ImmSetActiveContext( hwnd, old_himc, FALSE ); + ImmSetActiveContext( hwnd, new_himc, TRUE ); + } + + return ret == AICR_FAILED ? 0 : old_himc; +} + +static BOOL CALLBACK enum_associate_context( HWND hwnd, LPARAM lparam ) +{ + ImmAssociateContext( hwnd, (HIMC)lparam ); + return TRUE; +} + +/*********************************************************************** + * ImmAssociateContextEx (IMM32.@) + */ +BOOL WINAPI ImmAssociateContextEx( HWND hwnd, HIMC new_himc, DWORD flags ) +{ + HIMC old_himc; + UINT ret; + + TRACE( "hwnd %p, new_himc %p, flags %#lx\n", hwnd, new_himc, flags ); + + if (!hwnd) return FALSE; + + if (flags == IACE_CHILDREN) + { + EnumChildWindows( hwnd, enum_associate_context, (LPARAM)new_himc ); + return TRUE; + } + + old_himc = NtUserGetWindowInputContext( hwnd ); + ret = NtUserAssociateInputContext( hwnd, new_himc, flags ); + if (ret == AICR_FOCUS_CHANGED) + { + ImmSetActiveContext( hwnd, old_himc, FALSE ); + ImmSetActiveContext( hwnd, new_himc, TRUE ); + } + + return ret != AICR_FAILED; +} + struct enum_register_word_params_WtoA { REGISTERWORDENUMPROCA proc; From 936ba1e135d550fb9341ad97e26d2b96acb31b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Apr 2023 11:36:13 +0200 Subject: [PATCH 267/758] imm32: Update IME UI window IMMGWL_IMC when focus or HIMC changes. (cherry picked from commit 920154672d49241512cac9c49f92ceadfec759c8) --- dlls/imm32/imm.c | 24 +++++++++++++++++------- dlls/imm32/tests/imm32.c | 6 +++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index f7304897f15..7d4f4b4c13f 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -972,11 +972,18 @@ static HWND get_ime_ui_window(void) { imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, ImmGetDefaultIMEWnd( 0 ), 0, ime->module, 0 ); - SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)imc->handle ); + SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)NtUserGetWindowInputContext( GetFocus() ) ); } return imc->ui_hwnd; } +static void set_ime_ui_window_himc( HIMC himc ) +{ + HWND hwnd; + if (!(hwnd = get_ime_ui_window())) return; + SetWindowLongPtrW( hwnd, IMMGWL_IMC, (LONG_PTR)himc ); +} + /*********************************************************************** * ImmCreateContext (IMM32.@) */ @@ -1021,6 +1028,7 @@ HIMC WINAPI ImmAssociateContext( HWND hwnd, HIMC new_himc ) { ImmSetActiveContext( hwnd, old_himc, FALSE ); ImmSetActiveContext( hwnd, new_himc, TRUE ); + if (hwnd == GetFocus()) set_ime_ui_window_himc( new_himc ); } return ret == AICR_FAILED ? 0 : old_himc; @@ -1056,6 +1064,7 @@ BOOL WINAPI ImmAssociateContextEx( HWND hwnd, HIMC new_himc, DWORD flags ) { ImmSetActiveContext( hwnd, old_himc, FALSE ); ImmSetActiveContext( hwnd, new_himc, TRUE ); + if (hwnd == GetFocus()) set_ime_ui_window_himc( new_himc ); } return ret != AICR_FAILED; @@ -2683,15 +2692,12 @@ BOOL WINAPI ImmSetConversionStatus( HIMC himc, DWORD conversion, DWORD sentence BOOL WINAPI ImmSetOpenStatus( HIMC himc, BOOL status ) { INPUTCONTEXT *ctx; - HWND ui_hwnd; TRACE( "himc %p, status %u\n", himc, status ); if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; if (!(ctx = ImmLockIMC( himc ))) return FALSE; - if ((ui_hwnd = get_ime_ui_window())) SetWindowLongPtrW( ui_hwnd, IMMGWL_IMC, (LONG_PTR)himc ); - if (status != ctx->fOpen) { ctx->fOpen = status; @@ -3240,11 +3246,15 @@ static LRESULT ime_internal_msg( WPARAM wparam, LPARAM lparam) switch (wparam) { case IME_INTERNAL_ACTIVATE: + hwnd = (HWND)lparam; + himc = NtUserGetWindowInputContext( hwnd ); + ImmSetActiveContext( hwnd, himc, TRUE ); + set_ime_ui_window_himc( himc ); + break; case IME_INTERNAL_DEACTIVATE: hwnd = (HWND)lparam; - himc = ImmGetContext(hwnd); - ImmSetActiveContext(hwnd, himc, wparam == IME_INTERNAL_ACTIVATE); - ImmReleaseContext(hwnd, himc); + himc = NtUserGetWindowInputContext( hwnd ); + ImmSetActiveContext( hwnd, himc, FALSE ); break; case IME_INTERNAL_HKL_ACTIVATE: ImmEnumInputContext( 0, enum_activate_layout, 0 ); diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 9857583fafa..206d58ac9d8 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4566,9 +4566,9 @@ static void test_ImmSetOpenStatus(void) ok_ne( NULL, ime_windows.ime_ui_hwnd, HWND, "%p" ); ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) ); - todo_wine ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) ); - todo_wine ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_eq( default_himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 1, ImmDestroyContext( himc ) ); memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; @@ -5331,7 +5331,7 @@ static void test_ImmSetActiveContext(void) ctx->hWnd = 0; ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); - todo_wine ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); + ok_eq( himc, (HIMC)GetWindowLongPtrW( ime_windows.ime_ui_hwnd, IMMGWL_IMC ), HIMC, "%p" ); ok_ret( 0, IsWindowVisible( ime_windows.ime_ui_hwnd ) ); ok_eq( hwnd, ctx->hWnd, HWND, "%p" ); From c8d015f71b1eabbeebe495c5b2fc042eb25b1eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Apr 2023 12:05:07 +0200 Subject: [PATCH 268/758] imm32/tests: Add some ImmGenerateMessage tests. (cherry picked from commit 1d591d08fd253e8941a04262403df648039bd96f) --- dlls/imm32/tests/imm32.c | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 206d58ac9d8..344729b9866 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6612,6 +6612,109 @@ static void test_ImmSetCandidateWindow(void) ime_call_count = 0; } +static void test_ImmGenerateMessage(void) +{ + const struct ime_call generate_sequence[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_TEST_WIN, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR}, + .todo = TRUE, + }, + { + .hkl = expect_ime, .himc = default_himc, + .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR}, + .todo = TRUE, + }, + {0}, + }; + TRANSMSG *msgs, *tmp_msgs; + INPUTCONTEXT *ctx; + HIMC himc; + HKL hkl; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + + if (!(hkl = wineime_hkl)) return; + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + todo_wine ok_ret( 4, ImmGetIMCCSize( ctx->hMsgBuf ) ); + ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, sizeof(*msgs) ); + ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" ); + + msgs = ImmLockIMCC( ctx->hMsgBuf ); + ok_ne( NULL, msgs, TRANSMSG *, "%p" ); + msgs[0].message = WM_IME_COMPOSITION; + msgs[0].wParam = 0; + msgs[0].lParam = GCS_COMPSTR; + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + + ctx->hWnd = 0; + ctx->dwNumMsgBuf = 0; + ok_ret( 1, ImmGenerateMessage( himc ) ); + ok_seq( empty_sequence ); + ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); + ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + + ctx->dwNumMsgBuf = 1; + ok_ret( 1, ImmGenerateMessage( himc ) ); + todo_wine ok_seq( empty_sequence ); + ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" ); + todo_wine ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); + todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + + ctx->hWnd = hwnd; + ctx->dwNumMsgBuf = 0; + ok_ret( 1, ImmGenerateMessage( himc ) ); + ok_seq( empty_sequence ); + todo_wine ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); + todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + if (!tmp_msgs) ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, sizeof(*msgs) ); + + ctx->dwNumMsgBuf = 1; + ok_ret( 1, ImmGenerateMessage( himc ) ); + ok_seq( generate_sequence ); + ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" ); + todo_wine ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); + todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + + tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); + todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( default_hkl ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -6674,6 +6777,8 @@ START_TEST(imm32) test_ImmSetCompositionFont( FALSE ); test_ImmSetCandidateWindow(); + test_ImmGenerateMessage(); + if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); if (init()) From bdb59cb20b2751f2c213777fe82b81b8472dfb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Apr 2023 12:14:54 +0200 Subject: [PATCH 269/758] imm32: Send messages one by one in ImmGenerateMessage. (cherry picked from commit 08e2edce964ef6f58f3fd874bc5001f8b9ec0292) --- dlls/imm32/imm.c | 46 ++++++++++++---------------------------- dlls/imm32/tests/imm32.c | 23 +++++++++----------- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 7d4f4b4c13f..dfe81ddaaf7 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -828,13 +828,6 @@ static void imc_post_message( struct imc *imc, TRANSMSG *message ) PostMessageW( target, message->message, message->wParam, message->lParam ); } -static void imc_send_message( struct imc *imc, TRANSMSG *message ) -{ - HWND target; - if (!(target = GetFocus()) && !(target = imc->IMC.hWnd)) return; - SendMessageW( target, message->message, message->wParam, message->lParam ); -} - /*********************************************************************** * ImmSetActiveContext (IMM32.@) */ @@ -3049,36 +3042,25 @@ DWORD WINAPI ImmGetIMCCSize(HIMCC imcc) /*********************************************************************** * ImmGenerateMessage(IMM32.@) */ -BOOL WINAPI ImmGenerateMessage(HIMC hIMC) +BOOL WINAPI ImmGenerateMessage( HIMC himc ) { - struct imc *data = get_imc_data( hIMC ); - - if (!data) - { - SetLastError(ERROR_INVALID_HANDLE); - return FALSE; - } - - TRACE("%li messages queued\n",data->IMC.dwNumMsgBuf); - if (data->IMC.dwNumMsgBuf > 0) - { - LPTRANSMSG lpTransMsg; - HIMCC hMsgBuf; - DWORD i, dwNumMsgBuf; + INPUTCONTEXT *ctx; - /* We are going to detach our hMsgBuff so that if processing messages - generates new messages they go into a new buffer */ - hMsgBuf = data->IMC.hMsgBuf; - dwNumMsgBuf = data->IMC.dwNumMsgBuf; + TRACE( "himc %p\n", himc ); - data->IMC.hMsgBuf = ImmCreateIMCC(0); - data->IMC.dwNumMsgBuf = 0; + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; - lpTransMsg = ImmLockIMCC(hMsgBuf); - for (i = 0; i < dwNumMsgBuf; i++) imc_send_message( data, lpTransMsg + i ); - ImmUnlockIMCC(hMsgBuf); - ImmDestroyIMCC(hMsgBuf); + while (ctx->dwNumMsgBuf--) + { + TRANSMSG *msgs, msg; + if (!(msgs = ImmLockIMCC( ctx->hMsgBuf ))) return FALSE; + msg = msgs[0]; + memmove( msgs, msgs + 1, ctx->dwNumMsgBuf * sizeof(*msgs) ); + ImmUnlockIMCC( ctx->hMsgBuf ); + SendMessageW( ctx->hWnd, msg.message, msg.wParam, msg.lParam ); } + ctx->dwNumMsgBuf++; return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 344729b9866..093b79ff86b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1322,8 +1322,8 @@ static void test_cross_thread_himc(void) ok_ret( 1, ImmGenerateMessage( himc[1] ) ); - todo_wine ok_ret( 0, ImmGenerateMessage( params.himc[0] ) ); - todo_wine ok_ret( 0, ImmGenerateMessage( params.himc[1] ) ); + ok_ret( 0, ImmGenerateMessage( params.himc[0] ) ); + ok_ret( 0, ImmGenerateMessage( params.himc[1] ) ); /* ImmAssociateContext should fail with cross thread HWND or HIMC */ @@ -6619,12 +6619,10 @@ static void test_ImmGenerateMessage(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR}, - .todo = TRUE, }, { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_COMPOSITION, .wparam = 0, .lparam = GCS_COMPSTR}, - .todo = TRUE, }, {0}, }; @@ -6673,34 +6671,33 @@ static void test_ImmGenerateMessage(void) ctx->dwNumMsgBuf = 1; ok_ret( 1, ImmGenerateMessage( himc ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" ); - todo_wine ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); - todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); ctx->hWnd = hwnd; ctx->dwNumMsgBuf = 0; ok_ret( 1, ImmGenerateMessage( himc ) ); ok_seq( empty_sequence ); - todo_wine ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); - todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); - if (!tmp_msgs) ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, sizeof(*msgs) ); ctx->dwNumMsgBuf = 1; ok_ret( 1, ImmGenerateMessage( himc ) ); ok_seq( generate_sequence ); ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" ); - todo_wine ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); + ok_ret( sizeof(*msgs), ImmGetIMCCSize( ctx->hMsgBuf ) ); tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); - todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); tmp_msgs = ImmLockIMCC( ctx->hMsgBuf ); - todo_wine ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); + ok_eq( msgs, tmp_msgs, TRANSMSG *, "%p" ); ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); ok_ret( 1, ImmUnlockIMC( himc ) ); From d039150b2391d7fc7dcfad3bb03c112c3645f574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 4 May 2023 11:23:08 +0200 Subject: [PATCH 270/758] riched20: Update the editor IME position on GCS_RESULTSTR. So that a GCS_RESULTSTR followed by GCS_COMPSTR, without interruping the composition, begins inserting the new composition text after the result instead of before it. (cherry picked from commit de45bc33a73eac5dbc3502b4a43cd48399507c73) --- dlls/riched20/editor.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index c4b4d7e72be..e993646f5a3 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -4145,6 +4145,8 @@ LRESULT editor_handle_message( ME_TextEditor *editor, UINT msg, WPARAM wParam, if (dwIndex == GCS_COMPSTR) set_selection_cursors(editor,editor->imeStartIndex, editor->imeStartIndex + dwBufLen/sizeof(WCHAR)); + else + editor->imeStartIndex = ME_GetCursorOfs(&editor->pCursors[0]); } ME_ReleaseStyle(style); ME_CommitUndo(editor); From 74b1e0f74677a307acde5aae6e4eb7f01378b228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 1 May 2023 16:17:05 +0200 Subject: [PATCH 271/758] winex11: Rename preedit buffer and related variables. (cherry picked from commit cbe434299c9f65efb86b6e54c07be9a4b648bda7) --- dlls/winex11.drv/xim.c | 52 +++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index b7f5f696ba5..4372faed3d6 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -44,10 +44,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(xim); BOOL ximInComposeMode=FALSE; -/* moved here from imm32 for dll separation */ -static DWORD dwCompStringLength = 0; -static LPBYTE CompositionString = NULL; -static DWORD dwCompStringSize = 0; +static BYTE *ime_comp_buf; +static DWORD ime_comp_len; +static DWORD ime_comp_max; static XIMStyle input_style = 0; static XIMStyle input_style_req = XIMPreeditCallbacks | XIMStatusCallbacks; @@ -72,38 +71,36 @@ static const char *debugstr_xim_style( XIMStyle style ) return wine_dbg_sprintf( "%s", buffer ); } -static void X11DRV_ImmSetInternalString(UINT offset, UINT selLength, LPWSTR lpComp, UINT len) +static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len ) { /* Composition strings are edited in chunks */ - unsigned int byte_length = len * sizeof(WCHAR); + unsigned int byte_length = new_len * sizeof(WCHAR); unsigned int byte_offset = offset * sizeof(WCHAR); - unsigned int byte_selection = selLength * sizeof(WCHAR); + unsigned int byte_selection = old_len * sizeof(WCHAR); int byte_expansion = byte_length - byte_selection; - LPBYTE ptr_new; + BYTE *ptr_new; - TRACE("( %i, %i, %p, %d):\n", offset, selLength, lpComp, len ); + TRACE( "(%i, %i, %p, %d):\n", offset, old_len, text, new_len ); - if (byte_expansion + dwCompStringLength >= dwCompStringSize) + if (byte_expansion + ime_comp_len >= ime_comp_max) { - ptr_new = realloc( CompositionString, dwCompStringSize + byte_expansion ); - if (ptr_new == NULL) + if (!(ptr_new = realloc( ime_comp_buf, ime_comp_max + byte_expansion ))) { ERR("Couldn't expand composition string buffer\n"); return; } - CompositionString = ptr_new; - dwCompStringSize += byte_expansion; + ime_comp_buf = ptr_new; + ime_comp_max += byte_expansion; } - ptr_new = CompositionString + byte_offset; - memmove(ptr_new + byte_length, ptr_new + byte_selection, - dwCompStringLength - byte_offset - byte_selection); - if (lpComp) memcpy(ptr_new, lpComp, byte_length); - dwCompStringLength += byte_expansion; + ptr_new = ime_comp_buf + byte_offset; + memmove( ptr_new + byte_length, ptr_new + byte_selection, + ime_comp_len - byte_offset - byte_selection ); + if (text) memcpy( ptr_new, text, byte_length ); + ime_comp_len += byte_expansion; - x11drv_client_func( client_func_ime_set_composition_string, - CompositionString, dwCompStringLength ); + x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, ime_comp_len ); } void X11DRV_XIMLookupChars( const char *str, UINT count ) @@ -161,11 +158,10 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); ximInComposeMode = FALSE; - if (dwCompStringSize) - free( CompositionString ); - dwCompStringSize = 0; - dwCompStringLength = 0; - CompositionString = NULL; + free( ime_comp_buf ); + ime_comp_buf = NULL; + ime_comp_max = 0; + ime_comp_len = 0; x11drv_client_call( client_ime_set_composition_status, FALSE ); return 0; } @@ -181,7 +177,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (!params) return 0; if (!(text = params->text)) - X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, NULL, 0 ); + xim_update_comp_string( params->chg_first, params->chg_length, NULL, 0 ); else { size_t text_len; @@ -201,7 +197,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if ((output = malloc( text_len * sizeof(WCHAR) ))) { text_len = ntdll_umbstowcs( str, text_len, output, text_len ); - X11DRV_ImmSetInternalString( params->chg_first, params->chg_length, output, text_len ); + xim_update_comp_string( params->chg_first, params->chg_length, output, text_len ); free( output ); } From 88f11ffe5e79e95e55fc4c2986ea9d1c08f5c353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 2 May 2023 16:11:30 +0200 Subject: [PATCH 272/758] winex11: Simplify xic_preedit_draw control flow. (cherry picked from commit 6a36990f10f2b7d86c1802b95f93a93ec05f8393) --- dlls/winex11.drv/xim.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 4372faed3d6..b113dca20e9 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -170,40 +170,36 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) { XIMPreeditDrawCallbackStruct *params = (void *)arg; HWND hwnd = (HWND)user; + size_t text_len; XIMText *text; + WCHAR *output; + char *str; + int len; TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); if (!params) return 0; - if (!(text = params->text)) + if (!(text = params->text)) str = NULL; + else if (!text->encoding_is_wchar) str = text->string.multi_byte; + else if ((len = wcstombs( NULL, text->string.wide_char, text->length )) < 0) str = NULL; + else if ((str = malloc( len + 1 ))) + { + wcstombs( str, text->string.wide_char, len ); + str[len] = 0; + } + + if (!str || !(text_len = strlen( str )) || !(output = malloc( text_len * sizeof(WCHAR) ))) xim_update_comp_string( params->chg_first, params->chg_length, NULL, 0 ); else { - size_t text_len; - WCHAR *output; - char *str; - int len; - - if (!text->encoding_is_wchar) str = text->string.multi_byte; - else if ((len = wcstombs( NULL, text->string.wide_char, text->length )) < 0) str = NULL; - else if ((str = malloc( len + 1 ))) - { - wcstombs( str, text->string.wide_char, len ); - str[len] = 0; - } - - text_len = str ? strlen( str ) : 0; - if ((output = malloc( text_len * sizeof(WCHAR) ))) - { - text_len = ntdll_umbstowcs( str, text_len, output, text_len ); - xim_update_comp_string( params->chg_first, params->chg_length, output, text_len ); - free( output ); - } - - if (str != text->string.multi_byte) free( str ); + text_len = ntdll_umbstowcs( str, text_len, output, text_len ); + xim_update_comp_string( params->chg_first, params->chg_length, output, text_len ); + free( output ); } + if (text && str != text->string.multi_byte) free( str ); + x11drv_client_call( client_ime_set_cursor_pos, params->caret ); return 0; From 06d7ca3894f734fe740bbd61c49aa6d2ef5959a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 1 May 2023 16:27:10 +0200 Subject: [PATCH 273/758] winex11: Compute preedit text buffer sizes in WCHAR units. (cherry picked from commit 3723b7867a95e1be4c152365a5e0d532109d56a1) --- dlls/winex11.drv/xim.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index b113dca20e9..1aca49871e9 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -44,7 +44,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(xim); BOOL ximInComposeMode=FALSE; -static BYTE *ime_comp_buf; +static WCHAR *ime_comp_buf; static DWORD ime_comp_len; static DWORD ime_comp_max; @@ -73,34 +73,29 @@ static const char *debugstr_xim_style( XIMStyle style ) static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len ) { - /* Composition strings are edited in chunks */ - unsigned int byte_length = new_len * sizeof(WCHAR); - unsigned int byte_offset = offset * sizeof(WCHAR); - unsigned int byte_selection = old_len * sizeof(WCHAR); - int byte_expansion = byte_length - byte_selection; - BYTE *ptr_new; + int diff = new_len - old_len; + WCHAR *ptr; - TRACE( "(%i, %i, %p, %d):\n", offset, old_len, text, new_len ); + TRACE( "offset %u, old_len %u, text %s\n", offset, old_len, debugstr_wn(text, new_len) ); - if (byte_expansion + ime_comp_len >= ime_comp_max) + if (diff + ime_comp_len >= ime_comp_max) { - if (!(ptr_new = realloc( ime_comp_buf, ime_comp_max + byte_expansion ))) + if (!(ptr = realloc( ime_comp_buf, (ime_comp_max + diff) * sizeof(WCHAR) ))) { ERR("Couldn't expand composition string buffer\n"); return; } - ime_comp_buf = ptr_new; - ime_comp_max += byte_expansion; + ime_comp_buf = ptr; + ime_comp_max += diff; } - ptr_new = ime_comp_buf + byte_offset; - memmove( ptr_new + byte_length, ptr_new + byte_selection, - ime_comp_len - byte_offset - byte_selection ); - if (text) memcpy( ptr_new, text, byte_length ); - ime_comp_len += byte_expansion; + ptr = ime_comp_buf + offset; + memmove( ptr + new_len, ptr + old_len, (ime_comp_len - offset - old_len) * sizeof(WCHAR) ); + if (text) memcpy( ptr, text, new_len * sizeof(WCHAR) ); + ime_comp_len += diff; - x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, ime_comp_len ); + x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, ime_comp_len * sizeof(WCHAR) ); } void X11DRV_XIMLookupChars( const char *str, UINT count ) From e7b55126081a6d65878c6e45b302ff6505f18fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 2 May 2023 16:12:45 +0200 Subject: [PATCH 274/758] winex11: Always zero terminate XIM composition string buffer. (cherry picked from commit 0f16bf1daf3d8f451a54215178114b418b8e9c9d) --- dlls/winex11.drv/xim.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 1aca49871e9..3601cb8c680 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -45,8 +45,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(xim); BOOL ximInComposeMode=FALSE; static WCHAR *ime_comp_buf; -static DWORD ime_comp_len; -static DWORD ime_comp_max; static XIMStyle input_style = 0; static XIMStyle input_style_req = XIMPreeditCallbacks | XIMStatusCallbacks; @@ -73,29 +71,26 @@ static const char *debugstr_xim_style( XIMStyle style ) static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len ) { + UINT len = ime_comp_buf ? wcslen( ime_comp_buf ) : 0; int diff = new_len - old_len; WCHAR *ptr; TRACE( "offset %u, old_len %u, text %s\n", offset, old_len, debugstr_wn(text, new_len) ); - if (diff + ime_comp_len >= ime_comp_max) + if (!(ptr = realloc( ime_comp_buf, (len + max(0, diff) + 1) * sizeof(WCHAR) ))) { - if (!(ptr = realloc( ime_comp_buf, (ime_comp_max + diff) * sizeof(WCHAR) ))) - { - ERR("Couldn't expand composition string buffer\n"); - return; - } - - ime_comp_buf = ptr; - ime_comp_max += diff; + ERR( "Failed to reallocate composition string buffer\n" ); + return; } + ime_comp_buf = ptr; ptr = ime_comp_buf + offset; - memmove( ptr + new_len, ptr + old_len, (ime_comp_len - offset - old_len) * sizeof(WCHAR) ); + memmove( ptr + new_len, ptr + old_len, (len - offset - old_len) * sizeof(WCHAR) ); if (text) memcpy( ptr, text, new_len * sizeof(WCHAR) ); - ime_comp_len += diff; + len += diff; + ime_comp_buf[len] = 0; - x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, ime_comp_len * sizeof(WCHAR) ); + x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, len * sizeof(WCHAR) ); } void X11DRV_XIMLookupChars( const char *str, UINT count ) @@ -105,8 +100,9 @@ void X11DRV_XIMLookupChars( const char *str, UINT count ) TRACE("%p %u\n", str, count); - if (!(output = malloc( count * sizeof(WCHAR) ))) return; + if (!(output = malloc( (count + 1) * sizeof(WCHAR) ))) return; len = ntdll_umbstowcs( str, count, output, count ); + output[len] = 0; x11drv_client_func( client_func_ime_set_result, output, len * sizeof(WCHAR) ); free( output ); @@ -155,8 +151,7 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) ximInComposeMode = FALSE; free( ime_comp_buf ); ime_comp_buf = NULL; - ime_comp_max = 0; - ime_comp_len = 0; + x11drv_client_call( client_ime_set_composition_status, FALSE ); return 0; } From 60222b4862f137b98a6fd95da42ec4d43f07bde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 13:16:01 +0200 Subject: [PATCH 275/758] imm32/tests: Move IME calls test helpers around. (cherry picked from commit 4ccc47c0ca9fdf2a415fdaaa1328376bb2aacea0) --- dlls/imm32/tests/imm32.c | 738 +++++++++++++++++++-------------------- 1 file changed, 369 insertions(+), 369 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 093b79ff86b..d06ad1fae3d 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -263,6 +263,299 @@ static void process_messages(void) } } +#define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) + +static BOOL ImeSelect_init_status; +static BOOL todo_ImeInquire; +DEFINE_EXPECT( ImeInquire ); +static BOOL todo_ImeDestroy; +DEFINE_EXPECT( ImeDestroy ); +DEFINE_EXPECT( ImeEscape ); +DEFINE_EXPECT( ImeEnumRegisterWord ); +DEFINE_EXPECT( ImeRegisterWord ); +DEFINE_EXPECT( ImeGetRegisterWordStyle ); +DEFINE_EXPECT( ImeUnregisterWord ); +static BOOL todo_ImeSetCompositionString; +DEFINE_EXPECT( ImeSetCompositionString ); +static BOOL todo_IME_DLL_PROCESS_ATTACH; +DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); +static BOOL todo_IME_DLL_PROCESS_DETACH; +DEFINE_EXPECT( IME_DLL_PROCESS_DETACH ); + +static IMEINFO ime_info; +static UINT ime_count; +static WCHAR ime_path[MAX_PATH]; +static HIMC default_himc; +static HKL default_hkl, wineime_hkl; +static HKL expect_ime = (HKL)(int)0xe020047f; + +enum ime_function +{ + IME_SELECT = 1, + IME_NOTIFY, + IME_PROCESS_KEY, + IME_SET_ACTIVE_CONTEXT, + MSG_IME_UI, + MSG_TEST_WIN, +}; + +struct ime_call +{ + HKL hkl; + HIMC himc; + enum ime_function func; + + union + { + int select; + struct + { + int action; + int index; + int value; + } notify; + struct + { + WORD vkey; + LPARAM lparam; + } process_key; + struct + { + int flag; + } set_active_context; + struct + { + UINT msg; + WPARAM wparam; + LPARAM lparam; + } message; + }; + + BOOL todo; + BOOL broken; + BOOL flaky_himc; +}; + +struct ime_call empty_sequence[] = {{0}}; +static struct ime_call ime_calls[1024]; +static ULONG ime_call_count; + +#define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b ) +static int ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received ) +{ + int ret; + + if ((ret = expected->func - received->func)) goto done; + /* Wine doesn't allocate HIMC in a deterministic order, ignore them when they are enumerated */ + if (expected->flaky_himc && (ret = !!(UINT_PTR)expected->himc - !!(UINT_PTR)received->himc)) goto done; + if (!expected->flaky_himc && (ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done; + if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done; + switch (expected->func) + { + case IME_SELECT: + if ((ret = expected->select - received->select)) goto done; + break; + case IME_NOTIFY: + if ((ret = expected->notify.action - received->notify.action)) goto done; + if ((ret = expected->notify.index - received->notify.index)) goto done; + if ((ret = expected->notify.value - received->notify.value)) goto done; + break; + case IME_PROCESS_KEY: + if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done; + if ((ret = expected->process_key.lparam - received->process_key.lparam)) goto done; + break; + case IME_SET_ACTIVE_CONTEXT: + if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done; + break; + case MSG_IME_UI: + case MSG_TEST_WIN: + if ((ret = expected->message.msg - received->message.msg)) goto done; + if ((ret = (expected->message.wparam - received->message.wparam))) goto done; + if ((ret = (expected->message.lparam - received->message.lparam))) goto done; + break; + } + +done: + if (ret && broken( expected->broken )) return ret; + + switch (received->func) + { + case IME_SELECT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select ); + return ret; + case IME_NOTIFY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", + received->hkl, received->himc, received->notify.action, received->notify.index, + received->notify.value ); + return ret; + case IME_PROCESS_KEY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n", + received->hkl, received->himc, received->process_key.vkey, received->process_key.lparam ); + return ret; + case IME_SET_ACTIVE_CONTEXT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", received->hkl, received->himc, + received->set_active_context.flag ); + return ret; + case MSG_IME_UI: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, + received->himc, received->message.msg, received->message.wparam, received->message.lparam ); + return ret; + case MSG_TEST_WIN: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, + received->himc, received->message.msg, received->message.wparam, received->message.lparam ); + return ret; + } + + switch (expected->func) + { + case IME_SELECT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select ); + break; + case IME_NOTIFY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", + expected->hkl, expected->himc, expected->notify.action, expected->notify.index, + expected->notify.value ); + break; + case IME_PROCESS_KEY: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n", + expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.lparam ); + break; + case IME_SET_ACTIVE_CONTEXT: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", expected->hkl, expected->himc, + expected->set_active_context.flag ); + break; + case MSG_IME_UI: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, + expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); + break; + case MSG_TEST_WIN: + todo_wine_if( expected->todo ) + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, + expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); + break; + } + + return 0; +} + +#define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a ) +static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context ) +{ + const struct ime_call *received = ime_calls; + UINT i = 0, ret; + + while (expected->func || received->func) + { + winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "", + !received->func ? " (missing)" : "" ); + ret = ok_call_( file, line, expected, received ); + if (ret && expected->todo && expected->func && + !strcmp( winetest_platform, "wine" )) + expected++; + else if (ret && broken(expected->broken)) + expected++; + else + { + if (expected->func) expected++; + if (received->func) received++; + } + winetest_pop_context(); + } + + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + +static BOOL check_WM_SHOWWINDOW; + +static BOOL ignore_message( UINT msg ) +{ + switch (msg) + { + case WM_IME_STARTCOMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_COMPOSITION: + case WM_IME_SETCONTEXT: + case WM_IME_NOTIFY: + case WM_IME_CONTROL: + case WM_IME_COMPOSITIONFULL: + case WM_IME_SELECT: + case WM_IME_CHAR: + case 0x287: + case WM_IME_REQUEST: + case WM_IME_KEYDOWN: + case WM_IME_KEYUP: + return FALSE; + case WM_SHOWWINDOW: + return !check_WM_SHOWWINDOW; + default: + return TRUE; + } +} + +static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ), + .func = MSG_IME_UI, .message = {.msg = msg, .wparam = wparam, .lparam = lparam} + }; + LONG_PTR ptr; + + ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + + ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE ); + ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr ); + + ime_calls[ime_call_count++] = call; + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = ImmGetContext( hwnd ), + .func = MSG_TEST_WIN, .message = {.msg = msg, .wparam = wparam, .lparam = lparam} + }; + + ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + + ime_calls[ime_call_count++] = call; + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static WNDCLASSEXW ime_ui_class = +{ + .cbSize = sizeof(WNDCLASSEXW), + .style = CS_IME, + .lpfnWndProc = ime_ui_window_proc, + .cbWndExtra = 2 * sizeof(LONG_PTR), + .lpszClassName = L"WineTestIME", +}; + +static WNDCLASSEXW test_class = +{ + .cbSize = sizeof(WNDCLASSEXW), + .lpfnWndProc = test_window_proc, + .lpszClassName = L"WineTest", +}; + /* * msgspy - record and analyse message traces sent to a certain window */ @@ -2595,397 +2888,104 @@ static void test_com_initialization(void) test_apttype(hr == S_OK ? APTTYPE_MTA : APTTYPE_MAINSTA); DestroyWindow(wnd); test_apttype(-1); -} - -static DWORD WINAPI disable_ime_thread(void *arg) -{ - HWND h, def; - MSG msg; - BOOL r; - - h = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0); - ok(h != NULL, "CreateWindow failed\n"); - def = ImmGetDefaultIMEWnd(h); - ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n"); - - r = ImmDisableIME(arg ? GetCurrentThreadId() : 0); - ok(r, "ImmDisableIME failed\n"); - - if (arg) - { - def = ImmGetDefaultIMEWnd(h); - todo_wine ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n"); - while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) - DispatchMessageA(&msg); - } - def = ImmGetDefaultIMEWnd(h); - ok(!def, "ImmGetDefaultIMEWnd returned %p\n", def); - return 0; -} - -static DWORD WINAPI check_not_disabled_ime_thread(void *arg) -{ - HWND def, hwnd; - - WaitForSingleObject(arg, INFINITE); - hwnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0); - ok(hwnd != NULL, "CreateWindow failed\n"); - def = ImmGetDefaultIMEWnd(hwnd); - ok(def != NULL, "ImmGetDefaultIMEWnd returned %p\n", def); - return 0; -} - -static DWORD WINAPI disable_ime_process(void *arg) -{ - BOOL r = ImmDisableIME(-1); - ok(r, "ImmDisableIME failed\n"); - return 0; -} - -static void test_ImmDisableIME(void) -{ - HANDLE thread, event; - DWORD tid; - HWND def, def2; - BOOL r; - - def = ImmGetDefaultIMEWnd(hwnd); - ok(def != NULL, "ImmGetDefaultIMEWnd(hwnd) returned NULL\n"); - - event = CreateEventW(NULL, TRUE, FALSE, FALSE); - thread = CreateThread(NULL, 0, check_not_disabled_ime_thread, event, 0, &tid); - ok(thread != NULL, "CreateThread failed\n"); - r = ImmDisableIME(tid); - ok(!r, "ImmDisableIME(tid) succeeded\n"); - SetEvent(event); - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); - CloseHandle(event); - - thread = CreateThread(NULL, 0, disable_ime_thread, 0, 0, NULL); - ok(thread != NULL, "CreateThread failed\n"); - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); - - thread = CreateThread(NULL, 0, disable_ime_thread, (void*)1, 0, NULL); - ok(thread != NULL, "CreateThread failed\n"); - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); - - msg_spy_pump_msg_queue(); - thread = CreateThread(NULL, 0, disable_ime_process, 0, 0, NULL); - ok(thread != NULL, "CreateThread failed\n"); - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); - - ok(IsWindow(def), "not a window\n"); - def2 = ImmGetDefaultIMEWnd(hwnd); - ok(def2 == def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def2); - ok(IsWindow(def), "not a window\n"); - msg_spy_pump_msg_queue(); - ok(!IsWindow(def), "window is still valid\n"); - def = ImmGetDefaultIMEWnd(hwnd); - ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def); - - r = ImmDisableIME(-1); - ok(r, "ImmDisableIME(-1) failed\n"); - def = ImmGetDefaultIMEWnd(hwnd); - ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def); -} - -#define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) - -static BOOL ImeSelect_init_status; -static BOOL todo_ImeInquire; -DEFINE_EXPECT( ImeInquire ); -static BOOL todo_ImeDestroy; -DEFINE_EXPECT( ImeDestroy ); -DEFINE_EXPECT( ImeEscape ); -DEFINE_EXPECT( ImeEnumRegisterWord ); -DEFINE_EXPECT( ImeRegisterWord ); -DEFINE_EXPECT( ImeGetRegisterWordStyle ); -DEFINE_EXPECT( ImeUnregisterWord ); -static BOOL todo_ImeSetCompositionString; -DEFINE_EXPECT( ImeSetCompositionString ); -static BOOL todo_IME_DLL_PROCESS_ATTACH; -DEFINE_EXPECT( IME_DLL_PROCESS_ATTACH ); -static BOOL todo_IME_DLL_PROCESS_DETACH; -DEFINE_EXPECT( IME_DLL_PROCESS_DETACH ); - -static IMEINFO ime_info; -static UINT ime_count; -static WCHAR ime_path[MAX_PATH]; -static HIMC default_himc; -static HKL default_hkl, wineime_hkl; -static HKL expect_ime = (HKL)(int)0xe020047f; - -enum ime_function -{ - IME_SELECT = 1, - IME_NOTIFY, - IME_PROCESS_KEY, - IME_SET_ACTIVE_CONTEXT, - MSG_IME_UI, - MSG_TEST_WIN, -}; - -struct ime_call -{ - HKL hkl; - HIMC himc; - enum ime_function func; - - union - { - int select; - struct - { - int action; - int index; - int value; - } notify; - struct - { - WORD vkey; - LPARAM key_data; - } process_key; - struct - { - int flag; - } set_active_context; - struct - { - UINT msg; - WPARAM wparam; - LPARAM lparam; - } message; - }; - - BOOL todo; - BOOL broken; - BOOL flaky_himc; -}; - -struct ime_call empty_sequence[] = {{0}}; -static struct ime_call ime_calls[1024]; -static ULONG ime_call_count; +} -#define ok_call( a, b ) ok_call_( __FILE__, __LINE__, a, b ) -static int ok_call_( const char *file, int line, const struct ime_call *expected, const struct ime_call *received ) +static DWORD WINAPI disable_ime_thread(void *arg) { - int ret; - - if ((ret = expected->func - received->func)) goto done; - /* Wine doesn't allocate HIMC in a deterministic order, ignore them when they are enumerated */ - if (expected->flaky_himc && (ret = !!(UINT_PTR)expected->himc - !!(UINT_PTR)received->himc)) goto done; - if (!expected->flaky_himc && (ret = (UINT_PTR)expected->himc - (UINT_PTR)received->himc)) goto done; - if ((ret = (UINT)(UINT_PTR)expected->hkl - (UINT)(UINT_PTR)received->hkl)) goto done; - switch (expected->func) - { - case IME_SELECT: - if ((ret = expected->select - received->select)) goto done; - break; - case IME_NOTIFY: - if ((ret = expected->notify.action - received->notify.action)) goto done; - if ((ret = expected->notify.index - received->notify.index)) goto done; - if ((ret = expected->notify.value - received->notify.value)) goto done; - break; - case IME_PROCESS_KEY: - if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done; - if ((ret = expected->process_key.key_data - received->process_key.key_data)) goto done; - break; - case IME_SET_ACTIVE_CONTEXT: - if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done; - break; - case MSG_IME_UI: - case MSG_TEST_WIN: - if ((ret = expected->message.msg - received->message.msg)) goto done; - if ((ret = (expected->message.wparam - received->message.wparam))) goto done; - if ((ret = (expected->message.lparam - received->message.lparam))) goto done; - break; - } + HWND h, def; + MSG msg; + BOOL r; -done: - if (ret && broken( expected->broken )) return ret; + h = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0); + ok(h != NULL, "CreateWindow failed\n"); + def = ImmGetDefaultIMEWnd(h); + ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n"); - switch (received->func) - { - case IME_SELECT: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select ); - return ret; - case IME_NOTIFY: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", - received->hkl, received->himc, received->notify.action, received->notify.index, - received->notify.value ); - return ret; - case IME_PROCESS_KEY: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", - received->hkl, received->himc, received->process_key.vkey, received->process_key.key_data ); - return ret; - case IME_SET_ACTIVE_CONTEXT: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", received->hkl, received->himc, - received->set_active_context.flag ); - return ret; - case MSG_IME_UI: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, - received->himc, received->message.msg, received->message.wparam, received->message.lparam ); - return ret; - case MSG_TEST_WIN: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, - received->himc, received->message.msg, received->message.wparam, received->message.lparam ); - return ret; - } + r = ImmDisableIME(arg ? GetCurrentThreadId() : 0); + ok(r, "ImmDisableIME failed\n"); - switch (expected->func) + if (arg) { - case IME_SELECT: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select ); - break; - case IME_NOTIFY: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", - expected->hkl, expected->himc, expected->notify.action, expected->notify.index, - expected->notify.value ); - break; - case IME_PROCESS_KEY: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, key_data %#Ix\n", - expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.key_data ); - break; - case IME_SET_ACTIVE_CONTEXT: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", expected->hkl, expected->himc, - expected->set_active_context.flag ); - break; - case MSG_IME_UI: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, - expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); - break; - case MSG_TEST_WIN: - todo_wine_if( expected->todo ) - ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, - expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); - break; + def = ImmGetDefaultIMEWnd(h); + todo_wine ok(def != NULL, "ImmGetDefaultIMEWnd returned NULL\n"); + while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + DispatchMessageA(&msg); } - + def = ImmGetDefaultIMEWnd(h); + ok(!def, "ImmGetDefaultIMEWnd returned %p\n", def); return 0; } -#define ok_seq( a ) ok_seq_( __FILE__, __LINE__, a, #a ) -static void ok_seq_( const char *file, int line, const struct ime_call *expected, const char *context ) +static DWORD WINAPI check_not_disabled_ime_thread(void *arg) { - const struct ime_call *received = ime_calls; - UINT i = 0, ret; - - while (expected->func || received->func) - { - winetest_push_context( "%u%s%s", i++, !expected->func ? " (spurious)" : "", - !received->func ? " (missing)" : "" ); - ret = ok_call_( file, line, expected, received ); - if (ret && expected->todo && expected->func && - !strcmp( winetest_platform, "wine" )) - expected++; - else if (ret && broken(expected->broken)) - expected++; - else - { - if (expected->func) expected++; - if (received->func) received++; - } - winetest_pop_context(); - } + HWND def, hwnd; - memset( ime_calls, 0, sizeof(ime_calls) ); - ime_call_count = 0; + WaitForSingleObject(arg, INFINITE); + hwnd = CreateWindowA("static", "static", 0, 0, 0, 0, 0, 0, 0, 0, 0); + ok(hwnd != NULL, "CreateWindow failed\n"); + def = ImmGetDefaultIMEWnd(hwnd); + ok(def != NULL, "ImmGetDefaultIMEWnd returned %p\n", def); + return 0; } -static BOOL check_WM_SHOWWINDOW; - -static BOOL ignore_message( UINT msg ) +static DWORD WINAPI disable_ime_process(void *arg) { - switch (msg) - { - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_IME_SETCONTEXT: - case WM_IME_NOTIFY: - case WM_IME_CONTROL: - case WM_IME_COMPOSITIONFULL: - case WM_IME_SELECT: - case WM_IME_CHAR: - case 0x287: - case WM_IME_REQUEST: - case WM_IME_KEYDOWN: - case WM_IME_KEYUP: - return FALSE; - case WM_SHOWWINDOW: - return !check_WM_SHOWWINDOW; - default: - return TRUE; - } + BOOL r = ImmDisableIME(-1); + ok(r, "ImmDisableIME failed\n"); + return 0; } -static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +static void test_ImmDisableIME(void) { - struct ime_call call = - { - .hkl = GetKeyboardLayout( 0 ), .himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ), - .func = MSG_IME_UI, .message = {.msg = msg, .wparam = wparam, .lparam = lparam} - }; - LONG_PTR ptr; - - ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + HANDLE thread, event; + DWORD tid; + HWND def, def2; + BOOL r; - if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + def = ImmGetDefaultIMEWnd(hwnd); + ok(def != NULL, "ImmGetDefaultIMEWnd(hwnd) returned NULL\n"); - ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE ); - ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr ); + event = CreateEventW(NULL, TRUE, FALSE, FALSE); + thread = CreateThread(NULL, 0, check_not_disabled_ime_thread, event, 0, &tid); + ok(thread != NULL, "CreateThread failed\n"); + r = ImmDisableIME(tid); + ok(!r, "ImmDisableIME(tid) succeeded\n"); + SetEvent(event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + CloseHandle(event); - ime_calls[ime_call_count++] = call; - return DefWindowProcW( hwnd, msg, wparam, lparam ); -} + thread = CreateThread(NULL, 0, disable_ime_thread, 0, 0, NULL); + ok(thread != NULL, "CreateThread failed\n"); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); -static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) -{ - struct ime_call call = - { - .hkl = GetKeyboardLayout( 0 ), .himc = ImmGetContext( hwnd ), - .func = MSG_TEST_WIN, .message = {.msg = msg, .wparam = wparam, .lparam = lparam} - }; + thread = CreateThread(NULL, 0, disable_ime_thread, (void*)1, 0, NULL); + ok(thread != NULL, "CreateThread failed\n"); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); - ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + msg_spy_pump_msg_queue(); + thread = CreateThread(NULL, 0, disable_ime_process, 0, 0, NULL); + ok(thread != NULL, "CreateThread failed\n"); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); - if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + ok(IsWindow(def), "not a window\n"); + def2 = ImmGetDefaultIMEWnd(hwnd); + ok(def2 == def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def2); + ok(IsWindow(def), "not a window\n"); + msg_spy_pump_msg_queue(); + ok(!IsWindow(def), "window is still valid\n"); + def = ImmGetDefaultIMEWnd(hwnd); + ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def); - ime_calls[ime_call_count++] = call; - return DefWindowProcW( hwnd, msg, wparam, lparam ); + r = ImmDisableIME(-1); + ok(r, "ImmDisableIME(-1) failed\n"); + def = ImmGetDefaultIMEWnd(hwnd); + ok(!def, "ImmGetDefaultIMEWnd(hwnd) returned %p\n", def); } -static WNDCLASSEXW ime_ui_class = -{ - .cbSize = sizeof(WNDCLASSEXW), - .style = CS_IME, - .lpfnWndProc = ime_ui_window_proc, - .cbWndExtra = 2 * sizeof(LONG_PTR), - .lpszClassName = L"WineTestIME", -}; - -static WNDCLASSEXW test_class = -{ - .cbSize = sizeof(WNDCLASSEXW), - .lpfnWndProc = test_window_proc, - .lpszClassName = L"WineTest", -}; - static BOOL WINAPI ime_ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) { ime_trace( "hkl %p, hwnd %p, mode %lu, data %p\n", hkl, hwnd, mode, data ); @@ -3136,15 +3136,15 @@ static BOOL WINAPI ime_ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) return TRUE; } -static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) +static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state ) { struct ime_call call = { .hkl = GetKeyboardLayout( 0 ), .himc = himc, - .func = IME_PROCESS_KEY, .process_key = {.vkey = vkey, .key_data = key_data} + .func = IME_PROCESS_KEY, .process_key = {.vkey = vkey, .lparam = lparam} }; - ime_trace( "himc %p, vkey %u, key_data %#Ix, key_state %p\n", - himc, vkey, key_data, key_state ); + ime_trace( "himc %p, vkey %u, lparam %#Ix, state %p\n", + himc, vkey, lparam, state ); ime_calls[ime_call_count++] = call; return TRUE; } @@ -4644,7 +4644,7 @@ static void test_ImmProcessKey(void) { { .hkl = expect_ime, .himc = default_himc, - .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .key_data = 0}, + .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = 0}, }, {0}, }; From 480d3f120d9b6e6e2f2209e9699ad4311a9d8281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 13:02:36 +0200 Subject: [PATCH 276/758] imm32/tests: Test cross-thread ImmRequestMessage(W|A) calls. (cherry picked from commit c4187bc46bb39f9b5517fbb1feb0baf855d6effc) --- dlls/imm32/tests/imm32.c | 102 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index d06ad1fae3d..9290c9c5d0b 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1351,13 +1351,14 @@ static DWORD WINAPI test_cross_thread_himc_thread( void *arg ) POINT pos = {0}; MSG msg; - hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( NULL, hwnd, HWND, "%p" ); himc[0] = ImmGetContext( hwnd ); ok_ne( NULL, himc[0], HIMC, "%p" ); contexts[0] = ImmLockIMC( himc[0] ); ok_ne( NULL, contexts[0], INPUTCONTEXT *, "%p" ); + contexts[0]->hWnd = hwnd; tmp_hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -1370,6 +1371,7 @@ static DWORD WINAPI test_cross_thread_himc_thread( void *arg ) ok_ne( NULL, himc[1], HIMC, "%p" ); contexts[1] = ImmLockIMC( himc[1] ); ok_ne( NULL, contexts[1], INPUTCONTEXT *, "%p" ); + contexts[1]->hWnd = hwnd; ok_ret( 1, ImmSetOpenStatus( himc[0], 0xdeadbeef ) ); ok_ret( 1, ImmSetOpenStatus( himc[1], 0xfeedcafe ) ); @@ -1408,9 +1410,11 @@ static DWORD WINAPI test_cross_thread_himc_thread( void *arg ) static void test_cross_thread_himc(void) { static const WCHAR comp_string[] = L"CompString"; + RECONVERTSTRING reconv = {.dwSize = sizeof(RECONVERTSTRING)}; struct test_cross_thread_himc_params params; COMPOSITIONFORM composition = {0}; DWORD tid, conversion, sentence; + IMECHARPOSITION char_pos = {0}; CANDIDATEFORM candidate = {0}; COMPOSITIONSTRING *string; HIMC himc[2], tmp_himc; @@ -1434,6 +1438,9 @@ static void test_cross_thread_himc(void) ok_ne( NULL, thread, HANDLE, "%p" ); WaitForSingleObject( params.event, INFINITE ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + tmp_himc = ImmGetContext( params.hwnd ); ok_ne( himc[0], tmp_himc, HIMC, "%p" ); ok_eq( params.himc[0], tmp_himc, HIMC, "%p" ); @@ -1565,6 +1572,18 @@ static void test_cross_thread_himc(void) ok_ret( 1, ImmGetCompositionFontW( params.himc[0], &fontW ) ); ok_ret( 1, ImmGetCompositionFontW( params.himc[1], &fontW ) ); + /* ImmRequestMessage(W|A) should fail with cross thread HIMC */ + + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) ); + + todo_wine ok_seq( empty_sequence ); + /* ImmSetCompositionString(W|A) should fail with cross thread HIMC */ ok_ret( 10, ImmGetCompositionStringA( himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) ); @@ -1581,6 +1600,46 @@ static void test_cross_thread_himc(void) ok_ret( 20, ImmGetCompositionStringW( params.himc[0], GCS_COMPSTR, buffer, sizeof(buffer) ) ); ok_ret( 0, ImmGetCompositionStringW( params.himc[1], GCS_COMPSTR, buffer, sizeof(buffer) ) ); + /* ImmRequestMessage(W|A) should fail with cross thread HIMC */ + + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_RECONVERTSTRING, 0 ) ); + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_RECONVERTSTRING, 0 ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_RECONVERTSTRING, 0 ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_RECONVERTSTRING, 0 ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_RECONVERTSTRING, 0 ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_RECONVERTSTRING, 0 ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_RECONVERTSTRING, (LPARAM)&reconv ) ); + + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_DOCUMENTFEED, 0 ) ); + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_DOCUMENTFEED, 0 ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_DOCUMENTFEED, 0 ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_DOCUMENTFEED, 0 ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_DOCUMENTFEED, 0 ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_DOCUMENTFEED, 0 ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_DOCUMENTFEED, (LPARAM)&reconv ) ); + + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); + + todo_wine ok_seq( empty_sequence ); + /* ImmSetCompositionWindow should fail with cross thread HIMC */ ok_ret( 1, ImmSetCompositionWindow( himc[1], &composition ) ); @@ -1591,6 +1650,18 @@ static void test_cross_thread_himc(void) ok_ret( 1, ImmGetCompositionWindow( params.himc[0], &composition ) ); ok_ret( 1, ImmGetCompositionWindow( params.himc[1], &composition ) ); + /* ImmRequestMessage(W|A) should fail with cross thread HIMC */ + + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); + + todo_wine ok_seq( empty_sequence ); + /* ImmSetCandidateWindow should fail with cross thread HIMC */ ok_ret( 1, ImmSetCandidateWindow( himc[1], &candidate ) ); @@ -1601,6 +1672,23 @@ static void test_cross_thread_himc(void) ok_ret( 0, ImmSetCandidateWindow( params.himc[0], &candidate ) ); ok_ret( 0, ImmSetCandidateWindow( params.himc[1], &candidate ) ); + /* ImmRequestMessage(W|A) should fail with cross thread HIMC */ + + candidate.dwIndex = -1; + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + + candidate.dwIndex = 0; + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); + + todo_wine ok_seq( empty_sequence ); + /* ImmSetStatusWindowPos should fail with cross thread HIMC */ ok_ret( 1, ImmSetStatusWindowPos( himc[1], &pos ) ); @@ -1611,6 +1699,18 @@ static void test_cross_thread_himc(void) ok_ret( 1, ImmGetStatusWindowPos( params.himc[0], &pos ) ); ok_ret( 1, ImmGetStatusWindowPos( params.himc[1], &pos ) ); + /* ImmRequestMessage(W|A) should fail with cross thread HIMC */ + + ok_ret( 0, ImmRequestMessageW( himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + ok_ret( 0, ImmRequestMessageA( himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + + ok_ret( 0, ImmRequestMessageW( params.himc[0], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[0], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); + + todo_wine ok_seq( empty_sequence ); + /* ImmGenerateMessage should fail with cross thread HIMC */ ok_ret( 1, ImmGenerateMessage( himc[1] ) ); From 282bffd8691f9a592c99a09d1433b8e36e9a68d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 13:36:07 +0200 Subject: [PATCH 277/758] imm32: Use INPUTCONTEXT directly in ImmRequestMessage(W|A). (cherry picked from commit a117b9b202da3865603951d383a01e0d4e7a33cf) --- dlls/imm32/imm.c | 60 ++++++++++++++++++++++++++++++++-------- dlls/imm32/tests/imm32.c | 10 +++---- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index dfe81ddaaf7..08af055275e 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2380,31 +2380,67 @@ BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC) /*********************************************************************** * ImmRequestMessageA(IMM32.@) */ -LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam) +LRESULT WINAPI ImmRequestMessageA( HIMC himc, WPARAM wparam, LPARAM lparam ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; + LRESULT res; - TRACE("%p %Id %Id\n", hIMC, wParam, wParam); + TRACE( "himc %p, wparam %#Ix, lparam %#Ix\n", himc, wparam, lparam ); - if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam); + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; - SetLastError(ERROR_INVALID_HANDLE); - return 0; + switch (wparam) + { + case IMR_CANDIDATEWINDOW: + case IMR_COMPOSITIONFONT: + case IMR_COMPOSITIONWINDOW: + case IMR_CONFIRMRECONVERTSTRING: + case IMR_DOCUMENTFEED: + case IMR_QUERYCHARPOSITION: + case IMR_RECONVERTSTRING: + break; + default: + return FALSE; + } + + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + res = SendMessageA( ctx->hWnd, WM_IME_REQUEST, wparam, lparam ); + ImmUnlockIMC( himc ); + + return res; } /*********************************************************************** * ImmRequestMessageW(IMM32.@) */ -LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam) +LRESULT WINAPI ImmRequestMessageW( HIMC himc, WPARAM wparam, LPARAM lparam ) { - struct imc *data = get_imc_data( hIMC ); + INPUTCONTEXT *ctx; + LRESULT res; - TRACE("%p %Id %Id\n", hIMC, wParam, wParam); + TRACE( "himc %p, wparam %#Ix, lparam %#Ix\n", himc, wparam, lparam ); - if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam); + if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE; - SetLastError(ERROR_INVALID_HANDLE); - return 0; + switch (wparam) + { + case IMR_CANDIDATEWINDOW: + case IMR_COMPOSITIONFONT: + case IMR_COMPOSITIONWINDOW: + case IMR_CONFIRMRECONVERTSTRING: + case IMR_DOCUMENTFEED: + case IMR_QUERYCHARPOSITION: + case IMR_RECONVERTSTRING: + break; + default: + return FALSE; + } + + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + res = SendMessageW( ctx->hWnd, WM_IME_REQUEST, wparam, lparam ); + ImmUnlockIMC( himc ); + + return res; } /*********************************************************************** diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 9290c9c5d0b..5593f2d391c 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1582,7 +1582,7 @@ static void test_cross_thread_himc(void) ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontW ) ); ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONFONT, (LPARAM)&fontA ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); /* ImmSetCompositionString(W|A) should fail with cross thread HIMC */ @@ -1638,7 +1638,7 @@ static void test_cross_thread_himc(void) ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CONFIRMRECONVERTSTRING, (LPARAM)&reconv ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); /* ImmSetCompositionWindow should fail with cross thread HIMC */ @@ -1660,7 +1660,7 @@ static void test_cross_thread_himc(void) ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_COMPOSITIONWINDOW, (LPARAM)&composition ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); /* ImmSetCandidateWindow should fail with cross thread HIMC */ @@ -1687,7 +1687,7 @@ static void test_cross_thread_himc(void) ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_CANDIDATEWINDOW, (LPARAM)&candidate ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); /* ImmSetStatusWindowPos should fail with cross thread HIMC */ @@ -1709,7 +1709,7 @@ static void test_cross_thread_himc(void) ok_ret( 0, ImmRequestMessageW( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); ok_ret( 0, ImmRequestMessageA( params.himc[1], IMR_QUERYCHARPOSITION, (LPARAM)&char_pos ) ); - todo_wine ok_seq( empty_sequence ); + ok_seq( empty_sequence ); /* ImmGenerateMessage should fail with cross thread HIMC */ From a8e071c4e7342563d34559572c230650711fe992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 14:37:16 +0200 Subject: [PATCH 278/758] imm32/tests: Add more ImmProcessKey and ImmGetVirtualKey tests. (cherry picked from commit 655bd354eec1e21a4ded282cf59e3d93cb25ed5f) --- dlls/imm32/tests/imm32.c | 49 +++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 5593f2d391c..7841fc179f6 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3246,7 +3246,7 @@ static BOOL WINAPI ime_ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE ime_trace( "himc %p, vkey %u, lparam %#Ix, state %p\n", himc, vkey, lparam, state ); ime_calls[ime_call_count++] = call; - return TRUE; + return LOWORD(lparam); } static BOOL WINAPI ime_ImeRegisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) @@ -4740,23 +4740,40 @@ static void test_ImmSetOpenStatus(void) static void test_ImmProcessKey(void) { - const struct ime_call process_key_seq[] = + const struct ime_call process_key_0[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(0, 0x1e)}, + }, + {0}, + }; + const struct ime_call process_key_1[] = { { .hkl = expect_ime, .himc = default_himc, - .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = 0}, + .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(1, 0x1e)}, + }, + {0}, + }; + const struct ime_call process_key_2[] = + { + { + .hkl = expect_ime, .himc = default_himc, + .func = IME_PROCESS_KEY, .process_key = {.vkey = 'A', .lparam = MAKELONG(2, 0x1e)}, }, {0}, }; HKL hkl; UINT_PTR ret; + HIMC himc; hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); process_messages(); - ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', 0, 0 ) ); + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', MAKELONG(1, 0x1e), 0 ) ); ok_seq( empty_sequence ); ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -4769,19 +4786,35 @@ static void test_ImmProcessKey(void) memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; - ok_ret( 0, ImmProcessKey( 0, hkl, 'A', 0, 0 ) ); + ok_ret( 0, ImmProcessKey( 0, hkl, 'A', MAKELONG(1, 0x1e), 0 ) ); ok_seq( empty_sequence ); - ret = ImmProcessKey( hwnd, hkl, 'A', 0, 0 ); + ok_ret( 0, ImmProcessKey( hwnd, hkl, 'A', MAKELONG(0, 0x1e), 0 ) ); + ok_seq( process_key_0 ); + ret = ImmProcessKey( hwnd, hkl, 'A', MAKELONG(1, 0x1e), 0 ); todo_wine ok_ret( 2, ret ); - ok_seq( process_key_seq ); + ok_seq( process_key_1 ); + ok_ret( 2, ImmProcessKey( hwnd, hkl, 'A', MAKELONG(2, 0x1e), 0 ) ); + ok_seq( process_key_2 ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); - ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', 0, 0 ) ); + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'A', MAKELONG(1, 0x1e), 0 ) ); ok_seq( empty_sequence ); ok_eq( hkl, GetKeyboardLayout( 0 ), HKL, "%p" ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ok_ret( 'A', ImmGetVirtualKey( hwnd ) ); + ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); + todo_wine ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); + ok_eq( himc, ImmAssociateContext( hwnd, default_himc ), HIMC, "%p" ); + ok_ret( 'A', ImmGetVirtualKey( hwnd ) ); + ImmDestroyContext( himc ); + + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'A', 0 ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); + ok_ret( 1, ImmActivateLayout( default_hkl ) ); ok_ret( 1, ImmFreeLayout( hkl ) ); From 5d283d347d0115334525564306302c4f71ca3178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 14:42:22 +0200 Subject: [PATCH 279/758] imm32/tests: Test ImmTranslateMessage / ImeToAsciiEx calls. (cherry picked from commit 4b621dd9ae0dc891ac1bec2990db139df6830aae) --- dlls/imm32/tests/imm32.c | 354 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 18 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 7841fc179f6..b8bb1b72249 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -252,17 +252,37 @@ static void check_logfont_a_( int line, LOGFONTA *font, const LOGFONTA *expect ) DEFINE_EXPECT(WM_IME_SETCONTEXT_DEACTIVATE); DEFINE_EXPECT(WM_IME_SETCONTEXT_ACTIVATE); -static void process_messages(void) +#define process_messages() process_messages_(0) +static void process_messages_(HWND hwnd) { MSG msg; - while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) + while (PeekMessageA( &msg, hwnd, 0, 0, PM_REMOVE )) { TranslateMessage( &msg ); DispatchMessageA( &msg ); } } +/* try to make sure pending X events have been processed before continuing */ +#define flush_events() flush_events_( 100, 200 ) +static void flush_events_( int min_timeout, int max_timeout ) +{ + DWORD time = GetTickCount() + max_timeout; + MSG msg; + + while (max_timeout > 0) + { + if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break; + while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + } + max_timeout = time - GetTickCount(); + } +} + #define ime_trace( msg, ... ) if (winetest_debug > 1) trace( "%04lx:%s " msg, GetCurrentThreadId(), __func__, ## __VA_ARGS__ ) static BOOL ImeSelect_init_status; @@ -294,6 +314,7 @@ enum ime_function IME_SELECT = 1, IME_NOTIFY, IME_PROCESS_KEY, + IME_TO_ASCII_EX, IME_SET_ACTIVE_CONTEXT, MSG_IME_UI, MSG_TEST_WIN, @@ -320,6 +341,12 @@ struct ime_call LPARAM lparam; } process_key; struct + { + UINT vkey; + UINT vsc; + UINT flags; + } to_ascii_ex; + struct { int flag; } set_active_context; @@ -334,6 +361,7 @@ struct ime_call BOOL todo; BOOL broken; BOOL flaky_himc; + BOOL todo_value; }; struct ime_call empty_sequence[] = {{0}}; @@ -364,6 +392,11 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected if ((ret = expected->process_key.vkey - received->process_key.vkey)) goto done; if ((ret = expected->process_key.lparam - received->process_key.lparam)) goto done; break; + case IME_TO_ASCII_EX: + if ((ret = expected->to_ascii_ex.vkey - received->to_ascii_ex.vkey)) goto done; + if ((ret = expected->to_ascii_ex.vsc - received->to_ascii_ex.vsc)) goto done; + if ((ret = expected->to_ascii_ex.flags - received->to_ascii_ex.flags)) goto done; + break; case IME_SET_ACTIVE_CONTEXT: if ((ret = expected->set_active_context.flag - received->set_active_context.flag)) goto done; break; @@ -381,32 +414,38 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected switch (received->func) { case IME_SELECT: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SELECT select %u\n", received->hkl, received->himc, received->select ); return ret; case IME_NOTIFY: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", received->hkl, received->himc, received->notify.action, received->notify.index, received->notify.value ); return ret; case IME_PROCESS_KEY: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n", received->hkl, received->himc, received->process_key.vkey, received->process_key.lparam ); return ret; + case IME_TO_ASCII_EX: + todo_wine_if( expected->todo || expected->todo_value ) + ok_(file, line)( !ret, "got hkl %p, himc %p, IME_TO_ASCII_EX vkey %#x, vsc %#x, flags %#x\n", + received->hkl, received->himc, received->to_ascii_ex.vkey, received->to_ascii_ex.vsc, + received->to_ascii_ex.flags ); + return ret; case IME_SET_ACTIVE_CONTEXT: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", received->hkl, received->himc, received->set_active_context.flag ); return ret; case MSG_IME_UI: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, received->himc, received->message.msg, received->message.wparam, received->message.lparam ); return ret; case MSG_TEST_WIN: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, received->himc, received->message.msg, received->message.wparam, received->message.lparam ); return ret; @@ -415,32 +454,38 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected switch (expected->func) { case IME_SELECT: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "hkl %p, himc %p, IME_SELECT select %u\n", expected->hkl, expected->himc, expected->select ); break; case IME_NOTIFY: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "hkl %p, himc %p, IME_NOTIFY action %#x, index %#x, value %#x\n", expected->hkl, expected->himc, expected->notify.action, expected->notify.index, expected->notify.value ); break; case IME_PROCESS_KEY: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "hkl %p, himc %p, IME_PROCESS_KEY vkey %#x, lparam %#Ix\n", expected->hkl, expected->himc, expected->process_key.vkey, expected->process_key.lparam ); break; + case IME_TO_ASCII_EX: + todo_wine_if( expected->todo || expected->todo_value ) + ok_(file, line)( !ret, "hkl %p, himc %p, IME_TO_ASCII_EX vkey %#x, vsc %#x, flags %#x\n", + expected->hkl, expected->himc, expected->to_ascii_ex.vkey, expected->to_ascii_ex.vsc, + expected->to_ascii_ex.flags ); + break; case IME_SET_ACTIVE_CONTEXT: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "hkl %p, himc %p, IME_SET_ACTIVE_CONTEXT flag %u\n", expected->hkl, expected->himc, expected->set_active_context.flag ); break; case MSG_IME_UI: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); break; case MSG_TEST_WIN: - todo_wine_if( expected->todo ) + todo_wine_if( expected->todo || expected->todo_value ) ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); break; @@ -3394,11 +3439,70 @@ static BOOL WINAPI ime_ImeSetCompositionString( HIMC himc, DWORD index, const vo return TRUE; } -static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) +static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc ) { - ime_trace( "vkey %u, scan_code %u, key_state %p, msgs %p, state %u, himc %p\n", - vkey, scan_code, key_state, msgs, state, himc ); - return 0; + struct ime_call call = + { + .hkl = GetKeyboardLayout( 0 ), .himc = himc, + .func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = vkey, .vsc = vsc, .flags = flags} + }; + INPUTCONTEXT *ctx; + UINT count = 0; + + ime_trace( "vkey %#x, vsc %#x, state %p, msgs %p, flags %#x, himc %p\n", + vkey, vsc, state, msgs, flags, himc ); + ime_calls[ime_call_count++] = call; + + ok_ne( NULL, msgs, TRANSMSGLIST *, "%p" ); + todo_wine ok_eq( 256, msgs->uMsgCount, UINT, "%u" ); + + ctx = ImmLockIMC( himc ); + todo_wine ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( ctx->hWnd ) ); + + if (vsc & 0x200) + { + msgs->TransMsg[0].message = WM_IME_STARTCOMPOSITION; + msgs->TransMsg[0].wParam = 1; + msgs->TransMsg[0].lParam = 0; + count++; + msgs->TransMsg[1].message = WM_IME_ENDCOMPOSITION; + msgs->TransMsg[1].wParam = 1; + msgs->TransMsg[1].lParam = 0; + count++; + } + + if (vsc & 0x400) + { + TRANSMSG *msgs; + + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + + ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" ); + ok_eq( 0, ctx->dwNumMsgBuf, UINT, "%u" ); + + ctx->hMsgBuf = ImmReSizeIMCC( ctx->hMsgBuf, 64 * sizeof(*msgs) ); + ok_ne( NULL, ctx->hMsgBuf, HIMCC, "%p" ); + + msgs = ImmLockIMCC( ctx->hMsgBuf ); + ok_ne( NULL, msgs, TRANSMSG *, "%p" ); + + msgs[ctx->dwNumMsgBuf].message = WM_IME_STARTCOMPOSITION; + msgs[ctx->dwNumMsgBuf].wParam = 2; + msgs[ctx->dwNumMsgBuf].lParam = 0; + ctx->dwNumMsgBuf++; + msgs[ctx->dwNumMsgBuf].message = WM_IME_ENDCOMPOSITION; + msgs[ctx->dwNumMsgBuf].wParam = 2; + msgs[ctx->dwNumMsgBuf].lParam = 0; + ctx->dwNumMsgBuf++; + + ok_ret( 0, ImmUnlockIMCC( ctx->hMsgBuf ) ); + } + + ok_ret( 1, ImmUnlockIMC( himc ) ); + + if (vsc & 0x800) count = ~0; + return count; } static BOOL WINAPI ime_ImeUnregisterWord( const WCHAR *reading, DWORD style, const WCHAR *string ) @@ -6845,6 +6949,218 @@ static void test_ImmGenerateMessage(void) ime_call_count = 0; } +static void test_ImmTranslateMessage( BOOL kbd_char_first ) +{ + const struct ime_call process_key_seq[] = + { + { + .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x10)}, + }, + { + .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)}, + }, + {.todo = TRUE}, + }; + const struct ime_call to_ascii_ex_0[] = + { + { + .hkl = expect_ime, .himc = default_himc, .func = IME_TO_ASCII_EX, + .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x10}, + }, + {0}, + }; + const struct ime_call to_ascii_ex_1[] = + { + { + .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)}, + }, + { + .hkl = expect_ime, .himc = default_himc, .func = IME_TO_ASCII_EX, + /* FIXME what happened to kbd_char_first here!? */ + .to_ascii_ex = {.vkey = 'Q', .vsc = 0xc010}, + .todo_value = TRUE, + }, + {0}, + }; + struct ime_call to_ascii_ex_2[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x210)}, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, + .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x210}, + .todo_value = TRUE, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0x410)}, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, + .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x410}, + .todo_value = TRUE, + }, + {0}, + }; + struct ime_call to_ascii_ex_3[] = + { + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xa10)}, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, + .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xa10}, + .todo_value = TRUE, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY, + .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc10)}, + }, + { + .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, + .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xc10}, + .todo_value = TRUE, + }, + {0}, + }; + struct ime_call post_messages[] = + { + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 1}}, + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 1}}, + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 1}}, + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 1}}, + {0}, + }; + struct ime_call sent_messages[] = + { + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 2}}, + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 2}}, + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 2}}, + {.hkl = expect_ime, .himc = 0/*himc*/, .func = MSG_IME_UI, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 2}}, + {0}, + }; + HWND hwnd, other_hwnd; + INPUTCONTEXT *ctx; + HIMC himc; + HKL hkl; + UINT i, ret; + + ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; + if (kbd_char_first) ime_info.fdwProperty |= IME_PROP_KBD_CHAR_FIRST; + + winetest_push_context( kbd_char_first ? "kbd_char_first" : "default" ); + + if (!(hkl = wineime_hkl)) goto cleanup; + + other_hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!other_hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + flush_events(); + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + flush_events(); + + ok_ret( 1, ImmActivateLayout( hkl ) ); + ok_ret( 1, ImmLoadIME( hkl ) ); + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + process_messages(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x10), 0 ) ); + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) ); + + ok_ret( 0, ImmTranslateMessage( hwnd, 0, 0, 0 ) ); + todo_wine ok_ret( 'Q', ImmGetVirtualKey( hwnd ) ); + ok_seq( process_key_seq ); + + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'Q', MAKELONG(2, 0x10) ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); + todo_wine ok_seq( to_ascii_ex_0 ); + + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) ); + ok_seq( empty_sequence ); + + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); + ok_seq( to_ascii_ex_1 ); + + ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); + ok_eq( default_himc, ImmAssociateContext( other_hwnd, himc ), HIMC, "%p" ); + for (i = 0; i < ARRAY_SIZE(to_ascii_ex_2); i++) to_ascii_ex_2[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(to_ascii_ex_3); i++) to_ascii_ex_3[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(post_messages); i++) post_messages[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(sent_messages); i++) sent_messages[i].himc = himc; + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + ctx->hWnd = hwnd; + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) ); + ret = ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ); + todo_wine ok_eq( 1, ret, UINT, "%u" ); + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); + ok_seq( to_ascii_ex_2 ); + process_messages(); + todo_wine ok_seq( post_messages ); + ok_ret( 1, ImmGenerateMessage( himc ) ); + todo_wine ok_seq( sent_messages ); + + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xa10), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xa10) ) ); + ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc10), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc10) ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); + ok_seq( to_ascii_ex_3 ); + process_messages(); + ok_seq( empty_sequence ); + ok_ret( 1, ImmGenerateMessage( himc ) ); + todo_wine ok_seq( sent_messages ); + + ctx->hWnd = 0; + ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) ); + ret = ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ); + todo_wine ok_eq( 1, ret, UINT, "%u" ); + ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) ); + ok_ret( 0, ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( other_hwnd ) ); + ok_seq( to_ascii_ex_2 ); + process_messages_( hwnd ); + ok_seq( empty_sequence ); + process_messages_( other_hwnd ); + todo_wine ok_seq( post_messages ); + ok_ret( 1, ImmGenerateMessage( himc ) ); + ok_seq( empty_sequence ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, ImmActivateLayout( default_hkl ) ); + ok_ret( 1, DestroyWindow( other_hwnd ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + ok_ret( 1, ImmFreeLayout( hkl ) ); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + +cleanup: + winetest_pop_context(); +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -6908,6 +7224,8 @@ START_TEST(imm32) test_ImmSetCandidateWindow(); test_ImmGenerateMessage(); + test_ImmTranslateMessage( FALSE ); + test_ImmTranslateMessage( TRUE ); if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); From 49921bcc673792e4cfe34fd0a7508d8295ef05a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 23:26:32 +0200 Subject: [PATCH 280/758] imm32: Ignore some messages in ImmTranslateMessage. (cherry picked from commit bfb7799b74e6deac213e224bc384186d8e88282f) --- dlls/imm32/imm.c | 1 + dlls/imm32/tests/imm32.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 08af055275e..82d2ea4d897 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -3124,6 +3124,7 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + if (msg < WM_KEYDOWN || msg > WM_KEYUP) return FALSE; if (!(data = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; if (!(ime = imc_select_ime( data ))) return FALSE; if (data->lastVK == VK_PROCESSKEY) return FALSE; diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index b8bb1b72249..3f8736483d8 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -7082,12 +7082,12 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xc010), 0 ) ); ok_ret( 0, ImmTranslateMessage( hwnd, 0, 0, 0 ) ); - todo_wine ok_ret( 'Q', ImmGetVirtualKey( hwnd ) ); + ok_ret( 'Q', ImmGetVirtualKey( hwnd ) ); ok_seq( process_key_seq ); ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'Q', MAKELONG(2, 0x10) ) ); ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); - todo_wine ok_seq( to_ascii_ex_0 ); + ok_seq( to_ascii_ex_0 ); ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xc010) ) ); ok_seq( empty_sequence ); From 9a4fb65e4a3abb79326e02c5dba3b77b8e244e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 1 May 2023 11:37:50 +0200 Subject: [PATCH 281/758] imm32: Clear vkey before calling ToAsciiEx in ImmTranslateMessage. (cherry picked from commit 1c96ed9bd9f44bc6b2bf2ea5c4c38c2dbf1a3f1e) --- dlls/imm32/imm.c | 48 ++++++++++++++-------------------------- dlls/imm32/tests/imm32.c | 2 +- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 82d2ea4d897..8b086027fa0 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -85,8 +85,8 @@ struct imc DWORD dwLock; INPUTCONTEXT IMC; - struct ime *ime; - UINT lastVK; + struct ime *ime; + UINT vkey; HWND ui_hwnd; /* IME UI window, on the default input context */ }; @@ -2111,27 +2111,15 @@ BOOL WINAPI ImmGetStatusWindowPos( HIMC himc, POINT *pos ) /*********************************************************************** * ImmGetVirtualKey (IMM32.@) */ -UINT WINAPI ImmGetVirtualKey(HWND hWnd) -{ - OSVERSIONINFOA version; - struct imc *data = get_imc_data( ImmGetContext( hWnd ) ); - TRACE("%p\n", hWnd); - - if ( data ) - return data->lastVK; - - version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - GetVersionExA( &version ); - switch(version.dwPlatformId) - { - case VER_PLATFORM_WIN32_WINDOWS: - return VK_PROCESSKEY; - case VER_PLATFORM_WIN32_NT: - return 0; - default: - FIXME("%ld not supported\n",version.dwPlatformId); - return VK_PROCESSKEY; - } +UINT WINAPI ImmGetVirtualKey( HWND hwnd ) +{ + HIMC himc = ImmGetContext( hwnd ); + struct imc *imc; + + TRACE( "%p\n", hwnd ); + + if ((imc = get_imc_data( himc ))) return imc->vkey; + return VK_PROCESSKEY; } /*********************************************************************** @@ -3127,17 +3115,17 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar if (msg < WM_KEYDOWN || msg > WM_KEYUP) return FALSE; if (!(data = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE; if (!(ime = imc_select_ime( data ))) return FALSE; - if (data->lastVK == VK_PROCESSKEY) return FALSE; + if ((vkey = data->vkey) == VK_PROCESSKEY) return FALSE; + data->vkey = VK_PROCESSKEY; GetKeyboardState( state ); scan = lparam >> 0x10 & 0xff; - vkey = data->lastVK; if (ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { - if (!ime_is_unicode( ime )) ToAscii( data->lastVK, scan, state, &chr, 0 ); - else ToUnicodeEx( data->lastVK, scan, state, &chr, 1, 0, GetKeyboardLayout( 0 ) ); - vkey = MAKELONG( data->lastVK, chr ); + if (!ime_is_unicode( ime )) ToAscii( vkey, scan, state, &chr, 0 ); + else ToUnicodeEx( vkey, scan, state, &chr, 1, 0, GetKeyboardLayout( 0 ) ); + vkey = MAKELONG( vkey, chr ); } count = ime->pImeToAsciiEx( vkey, scan, state, &buffer.list, 0, data->handle ); @@ -3146,8 +3134,6 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar if (count > ARRAY_SIZE(buffer.TransMsg)) ImmGenerateMessage( data->handle ); else for (i = 0; i < count; i++) imc_post_message( data, buffer.TransMsg + i ); - data->lastVK = VK_PROCESSKEY; - return count > 0; } @@ -3171,7 +3157,7 @@ BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM lparam, DWORD u GetKeyboardState( state ); ret = ime->pImeProcessKey( imc->handle, vkey, lparam, state ); - imc->lastVK = ret ? vkey : VK_PROCESSKEY; + imc->vkey = ret ? vkey : VK_PROCESSKEY; return ret; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 3f8736483d8..d7748c53df2 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -3457,7 +3457,7 @@ static UINT WINAPI ime_ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGL todo_wine ok_eq( 256, msgs->uMsgCount, UINT, "%u" ); ctx = ImmLockIMC( himc ); - todo_wine ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( ctx->hWnd ) ); + ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( ctx->hWnd ) ); if (vsc & 0x200) { From 2eed4b13d2e7285b4eb496250eb365c68a079d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 1 May 2023 11:34:58 +0200 Subject: [PATCH 282/758] imm32: Post messages to the target window in ImmTranslateMessage. (cherry picked from commit f99ad772d1d5d43204d6ca730c72fda21bebdafc) --- dlls/imm32/imm.c | 16 +++++----------- dlls/imm32/tests/imm32.c | 22 ++++++++-------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 8b086027fa0..2fe355da5bb 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -821,13 +821,6 @@ BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) return TRUE; } -static void imc_post_message( struct imc *imc, TRANSMSG *message ) -{ - HWND target; - if (!(target = GetFocus()) && !(target = imc->IMC.hWnd)) return; - PostMessageW( target, message->message, message->wParam, message->lParam ); -} - /*********************************************************************** * ImmSetActiveContext (IMM32.@) */ @@ -3104,6 +3097,7 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar }; TRANSMSGLIST list; } buffer = {.uMsgCount = ARRAY_SIZE(buffer.TransMsg)}; + TRANSMSG *msgs = buffer.TransMsg; UINT scan, vkey, count, i; struct imc *data; struct ime *ime; @@ -3119,7 +3113,7 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar if ((vkey = data->vkey) == VK_PROCESSKEY) return FALSE; data->vkey = VK_PROCESSKEY; GetKeyboardState( state ); - scan = lparam >> 0x10 & 0xff; + scan = lparam >> 0x10; if (ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST) { @@ -3129,10 +3123,10 @@ BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpar } count = ime->pImeToAsciiEx( vkey, scan, state, &buffer.list, 0, data->handle ); - TRACE( "%u messages generated\n", count ); + if (count >= ARRAY_SIZE(buffer.TransMsg)) return 0; - if (count > ARRAY_SIZE(buffer.TransMsg)) ImmGenerateMessage( data->handle ); - else for (i = 0; i < count; i++) imc_post_message( data, buffer.TransMsg + i ); + for (i = 0; i < count; i++) PostMessageW( hwnd, msgs[i].message, msgs[i].wParam, msgs[i].lParam ); + TRACE( "%u messages generated\n", count ); return count > 0; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index d7748c53df2..990d410f985 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6961,7 +6961,7 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) .hkl = expect_ime, .himc = default_himc, .func = IME_PROCESS_KEY, .process_key = {.vkey = 'Q', .lparam = MAKELONG(2, 0xc010)}, }, - {.todo = TRUE}, + {0}, }; const struct ime_call to_ascii_ex_0[] = { @@ -6994,7 +6994,6 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x210}, - .todo_value = TRUE, }, { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY, @@ -7003,7 +7002,6 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0x410}, - .todo_value = TRUE, }, {0}, }; @@ -7016,7 +7014,6 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xa10}, - .todo_value = TRUE, }, { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_PROCESS_KEY, @@ -7025,7 +7022,6 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) { .hkl = expect_ime, .himc = 0/*himc*/, .func = IME_TO_ASCII_EX, .to_ascii_ex = {.vkey = kbd_char_first ? MAKELONG('Q', 'q') : 'Q', .vsc = 0xc10}, - .todo_value = TRUE, }, {0}, }; @@ -7049,7 +7045,7 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) INPUTCONTEXT *ctx; HIMC himc; HKL hkl; - UINT i, ret; + UINT i; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; if (kbd_char_first) ime_info.fdwProperty |= IME_PROP_KBD_CHAR_FIRST; @@ -7108,16 +7104,15 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) ctx->hWnd = hwnd; ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) ); - ret = ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ); - todo_wine ok_eq( 1, ret, UINT, "%u" ); + ok_ret( 1, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ) ); ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) ); ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) ); ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( hwnd ) ); ok_seq( to_ascii_ex_2 ); process_messages(); - todo_wine ok_seq( post_messages ); + ok_seq( post_messages ); ok_ret( 1, ImmGenerateMessage( himc ) ); - todo_wine ok_seq( sent_messages ); + ok_seq( sent_messages ); ok_ret( 2, ImmProcessKey( hwnd, expect_ime, 'Q', MAKELONG(2, 0xa10), 0 ) ); ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0xa10) ) ); @@ -7128,12 +7123,11 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) process_messages(); ok_seq( empty_sequence ); ok_ret( 1, ImmGenerateMessage( himc ) ); - todo_wine ok_seq( sent_messages ); + ok_seq( sent_messages ); ctx->hWnd = 0; ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x210), 0 ) ); - ret = ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ); - todo_wine ok_eq( 1, ret, UINT, "%u" ); + ok_ret( 1, ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x210) ) ); ok_ret( 2, ImmProcessKey( other_hwnd, expect_ime, 'Q', MAKELONG(2, 0x410), 0 ) ); ok_ret( 0, ImmTranslateMessage( other_hwnd, WM_KEYUP, 'Q', MAKELONG(2, 0x410) ) ); ok_ret( VK_PROCESSKEY, ImmGetVirtualKey( other_hwnd ) ); @@ -7141,7 +7135,7 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) process_messages_( hwnd ); ok_seq( empty_sequence ); process_messages_( other_hwnd ); - todo_wine ok_seq( post_messages ); + ok_seq( post_messages ); ok_ret( 1, ImmGenerateMessage( himc ) ); ok_seq( empty_sequence ); From 16882326eecf94bf0c728905b0524ba495c3535b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 5 May 2023 08:45:34 +0200 Subject: [PATCH 283/758] imm32/tests: Adjust the ImmSetOpenStatus tests for MS Korean IME. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54864 (cherry picked from commit 2fe97d51787ca0f8f85f92a5e328d91f53f2cb78) --- dlls/imm32/tests/imm32.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 990d410f985..7e8af4094ca 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4762,6 +4762,8 @@ static void test_ImmSetOpenStatus(void) ok_eq( 0xdeadbeef, status, UINT, "%#x" ); ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) ); + ok_seq( empty_sequence ); himc = ImmCreateContext(); ok_ne( NULL, himc, HIMC, "%p" ); @@ -4777,13 +4779,9 @@ static void test_ImmSetOpenStatus(void) memset( ime_calls, 0, sizeof(ime_calls) ); ime_call_count = 0; - - ok_ret( 1, ImmSetOpenStatus( default_himc, 0xdeadbeef ) ); - ok_seq( empty_sequence ); - status = ImmGetOpenStatus( default_himc ); - ok_eq( 0xdeadbeef, status, UINT, "%#x" ); - ok_eq( 0xdeadbeef, ctx->fOpen, UINT, "%#x" ); + ok( status == 0xdeadbeef || status == 0 /* MS Korean IME */, "got status %#lx\n", status ); + ok_eq( status, ctx->fOpen, UINT, "%#x" ); ctx->hWnd = 0; ok_ret( 1, ImmSetOpenStatus( default_himc, 0xfeedcafe ) ); From 455deaf97cd33bca327a42199112028b2d3b0e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 20:18:20 +0200 Subject: [PATCH 284/758] imm32/tests: Print human readable IME message names. (cherry picked from commit fc7203fb136edf7f9e7460bb2534fe2052ea27c0) --- dlls/imm32/tests/imm32.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 7e8af4094ca..78cae50dbbb 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -35,6 +35,26 @@ #include "ime_test.h" +static const char *debugstr_wm_ime( UINT msg ) +{ + switch (msg) + { + case WM_IME_STARTCOMPOSITION: return "WM_IME_STARTCOMPOSITION"; + case WM_IME_ENDCOMPOSITION: return "WM_IME_ENDCOMPOSITION"; + case WM_IME_COMPOSITION: return "WM_IME_COMPOSITION"; + case WM_IME_SETCONTEXT: return "WM_IME_SETCONTEXT"; + case WM_IME_NOTIFY: return "WM_IME_NOTIFY"; + case WM_IME_CONTROL: return "WM_IME_CONTROL"; + case WM_IME_COMPOSITIONFULL: return "WM_IME_COMPOSITIONFULL"; + case WM_IME_SELECT: return "WM_IME_SELECT"; + case WM_IME_CHAR: return "WM_IME_CHAR"; + case WM_IME_REQUEST: return "WM_IME_REQUEST"; + case WM_IME_KEYDOWN: return "WM_IME_KEYDOWN"; + case WM_IME_KEYUP: return "WM_IME_KEYUP"; + default: return wine_dbg_sprintf( "%#x", msg ); + } +} + static const char *debugstr_ok( const char *cond ) { int c, n = 0; @@ -441,13 +461,13 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected return ret; case MSG_IME_UI: todo_wine_if( expected->todo || expected->todo_value ) - ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, - received->himc, received->message.msg, received->message.wparam, received->message.lparam ); + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_IME_UI msg %s, wparam %#Ix, lparam %#Ix\n", received->hkl, + received->himc, debugstr_wm_ime(received->message.msg), received->message.wparam, received->message.lparam ); return ret; case MSG_TEST_WIN: todo_wine_if( expected->todo || expected->todo_value ) - ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", received->hkl, - received->himc, received->message.msg, received->message.wparam, received->message.lparam ); + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix\n", received->hkl, + received->himc, debugstr_wm_ime(received->message.msg), received->message.wparam, received->message.lparam ); return ret; } @@ -481,13 +501,13 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected break; case MSG_IME_UI: todo_wine_if( expected->todo || expected->todo_value ) - ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, - expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_IME_UI msg %s, wparam %#Ix, lparam %#Ix\n", expected->hkl, + expected->himc, debugstr_wm_ime(expected->message.msg), expected->message.wparam, expected->message.lparam ); break; case MSG_TEST_WIN: todo_wine_if( expected->todo || expected->todo_value ) - ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %#x, wparam %#Ix, lparam %#Ix\n", expected->hkl, - expected->himc, expected->message.msg, expected->message.wparam, expected->message.lparam ); + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix\n", expected->hkl, + expected->himc, debugstr_wm_ime(expected->message.msg), expected->message.wparam, expected->message.lparam ); break; } From 1a51db6edfac7d95d85ac2787960d7a58dc622ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 20:45:28 +0200 Subject: [PATCH 285/758] imm32/tests: Ignore some unknown WM_IME_NOTIFY messages. (cherry picked from commit 24ccd03e3ef71bbf77a625f0fb80bbd9b7423887) --- dlls/imm32/tests/imm32.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 78cae50dbbb..c7805dec996 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -544,15 +544,16 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected static BOOL check_WM_SHOWWINDOW; -static BOOL ignore_message( UINT msg ) +static BOOL ignore_message( UINT msg, WPARAM wparam ) { switch (msg) { + case WM_IME_NOTIFY: + return wparam > IMN_PRIVATE; case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION: case WM_IME_COMPOSITION: case WM_IME_SETCONTEXT: - case WM_IME_NOTIFY: case WM_IME_CONTROL: case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: @@ -580,7 +581,7 @@ static LRESULT CALLBACK ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); - if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + if (ignore_message( msg, wparam )) return DefWindowProcW( hwnd, msg, wparam, lparam ); ptr = GetWindowLongPtrW( hwnd, IMMGWL_PRIVATE ); ok( !ptr, "got IMMGWL_PRIVATE %#Ix\n", ptr ); @@ -599,7 +600,7 @@ static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP ime_trace( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); - if (ignore_message( msg )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + if (ignore_message( msg, wparam )) return DefWindowProcW( hwnd, msg, wparam, lparam ); ime_calls[ime_call_count++] = call; return DefWindowProcW( hwnd, msg, wparam, lparam ); From cf77b306988da43283e578670c846989e6c76ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 5 May 2023 07:46:26 +0200 Subject: [PATCH 286/758] imm32/tests: Add some missing local variables declarations. (cherry picked from commit 828fc9f50df2e55e9a8005f395e633bb59d9d17d) --- dlls/imm32/tests/imm32.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index c7805dec996..a6b57c123b7 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -4567,6 +4567,7 @@ static void test_ImmSetConversionStatus(void) DWORD old_conversion, old_sentence, conversion, sentence; HKL hkl; INPUTCONTEXT *ctx; + HWND hwnd; ok_ret( 0, ImmGetConversionStatus( 0, &old_conversion, &old_sentence ) ); ok_ret( 1, ImmGetConversionStatus( default_himc, &old_conversion, &old_sentence ) ); @@ -4737,6 +4738,7 @@ static void test_ImmSetOpenStatus(void) DWORD old_status, status; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; ok_ret( 0, ImmGetOpenStatus( 0 ) ); old_status = ImmGetOpenStatus( default_himc ); @@ -4890,6 +4892,7 @@ static void test_ImmProcessKey(void) HKL hkl; UINT_PTR ret; HIMC himc; + HWND hwnd; hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); @@ -5046,6 +5049,7 @@ static void test_ImmActivateLayout(void) HKL hkl; struct ime_windows ime_windows = {0}; HIMC himc; + HWND hwnd; UINT ret; SET_ENABLE( ImeInquire, TRUE ); @@ -5384,6 +5388,7 @@ static void test_DefWindowProc(void) }; HKL hkl; UINT_PTR ret; + HWND hwnd; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -5524,6 +5529,7 @@ static void test_ImmSetActiveContext(void) struct ime_windows ime_windows = {0}; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -5674,6 +5680,7 @@ static void test_ImmRequestMessage(void) LOGFONTW log_font = {0}; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; if (!(hkl = wineime_hkl)) return; @@ -5751,6 +5758,7 @@ static void test_ImmGetCandidateList( BOOL unicode ) CANDIDATEINFO *cand_info; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; expect_listW = (CANDIDATELIST *)expect_bufW; expect_listW->dwSize = offsetof(CANDIDATELIST, dwOffset[2]) + 32 * sizeof(WCHAR); @@ -5878,6 +5886,7 @@ static void test_ImmGetCandidateListCount( BOOL unicode ) INPUTCONTEXT *ctx; DWORD count; HIMC himc; + HWND hwnd; winetest_push_context( unicode ? "unicode" : "ansi" ); @@ -5957,6 +5966,7 @@ static void test_ImmGetCandidateWindow(void) CANDIDATEFORM cand_form; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -6109,6 +6119,7 @@ static void test_ImmGetCompositionString( BOOL unicode ) UINT i, len; BYTE *dst; HIMC himc; + HWND hwnd; SET_ENABLE( ImeSetCompositionString, TRUE ); @@ -6429,6 +6440,7 @@ static void test_ImmSetCompositionWindow(void) struct ime_windows ime_windows = {0}; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; HKL hkl; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -6543,6 +6555,7 @@ static void test_ImmSetStatusWindowPos(void) INPUTCONTEXT *ctx; POINT pos; HIMC himc; + HWND hwnd; HKL hkl; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -6674,6 +6687,7 @@ static void test_ImmSetCompositionFont( BOOL unicode ) }; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; HKL hkl; winetest_push_context( unicode ? "unicode" : "ansi" ); @@ -6802,6 +6816,7 @@ static void test_ImmSetCandidateWindow(void) }; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; HKL hkl; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; @@ -6885,6 +6900,7 @@ static void test_ImmGenerateMessage(void) TRANSMSG *msgs, *tmp_msgs; INPUTCONTEXT *ctx; HIMC himc; + HWND hwnd; HKL hkl; ime_info.fdwProperty = IME_PROP_END_UNLOAD | IME_PROP_UNICODE; From ba93e9e769b9418bb4795da3b663618ea9d9b208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 20:25:51 +0200 Subject: [PATCH 287/758] imm32/tests: Test MS Korean IME GA-NA-DA sequence. Credits to Byeong-Sik Jeon for the sequence, thanks! (cherry picked from commit 2024e45b9f17c62913c8f3837f7fa5ee8d589c26) --- dlls/imm32/tests/imm32.c | 279 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 275 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index a6b57c123b7..03bf91286a5 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -346,6 +346,9 @@ struct ime_call HIMC himc; enum ime_function func; + WCHAR comp[16]; + WCHAR result[16]; + union { int select; @@ -425,6 +428,8 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected if ((ret = expected->message.msg - received->message.msg)) goto done; if ((ret = (expected->message.wparam - received->message.wparam))) goto done; if ((ret = (expected->message.lparam - received->message.lparam))) goto done; + if ((ret = wcscmp( expected->comp, received->comp ))) goto done; + if ((ret = wcscmp( expected->result, received->result ))) goto done; break; } @@ -466,8 +471,9 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected return ret; case MSG_TEST_WIN: todo_wine_if( expected->todo || expected->todo_value ) - ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix\n", received->hkl, - received->himc, debugstr_wm_ime(received->message.msg), received->message.wparam, received->message.lparam ); + ok_(file, line)( !ret, "got hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix, comp %s, result %s\n", received->hkl, + received->himc, debugstr_wm_ime(received->message.msg), received->message.wparam, received->message.lparam, + debugstr_w(received->comp), debugstr_w(received->result) ); return ret; } @@ -506,8 +512,9 @@ static int ok_call_( const char *file, int line, const struct ime_call *expected break; case MSG_TEST_WIN: todo_wine_if( expected->todo || expected->todo_value ) - ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix\n", expected->hkl, - expected->himc, debugstr_wm_ime(expected->message.msg), expected->message.wparam, expected->message.lparam ); + ok_(file, line)( !ret, "hkl %p, himc %p, MSG_TEST_WIN msg %s, wparam %#Ix, lparam %#Ix, comp %s, result %s\n", expected->hkl, + expected->himc, debugstr_wm_ime(expected->message.msg), expected->message.wparam, expected->message.lparam, + debugstr_w(expected->comp), debugstr_w(expected->result) ); break; } @@ -602,6 +609,12 @@ static LRESULT CALLBACK test_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP if (ignore_message( msg, wparam )) return DefWindowProcW( hwnd, msg, wparam, lparam ); + if (msg == WM_IME_COMPOSITION) + { + ImmGetCompositionStringW( call.himc, GCS_COMPSTR, call.comp, sizeof(call.comp) ); + ImmGetCompositionStringW( call.himc, GCS_RESULTSTR, call.result, sizeof(call.result) ); + } + ime_calls[ime_call_count++] = call; return DefWindowProcW( hwnd, msg, wparam, lparam ); } @@ -7190,6 +7203,262 @@ static void test_ImmTranslateMessage( BOOL kbd_char_first ) winetest_pop_context(); } +static void test_ga_na_da(void) +{ + /* These sequences have some additional WM_IME_NOTIFY messages with unknown wparam > IMN_PRIVATE */ + struct ime_call complete_seq[] = + { + /* G */ + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + /* A */ + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac00", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + /* N */ + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac04", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac04, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + /* A */ + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_RESULTSTR}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xac00, .lparam = 0x1}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + /* D */ + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub09f", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb09f, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + /* A */ + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_RESULTSTR}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb098, .lparam = 0x1}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + /* RETURN */ + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\ub2e4", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_RESULTSTR}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb2e4, .lparam = 0x1}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_KEYDOWN, .wparam = 0xd, .lparam = 0x1c0001}}, + {0}, + }; + struct ime_call partial_g_seq[] = + { + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + {0}, + }; + struct ime_call partial_ga_seq[] = + { + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac00", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + {0}, + }; + struct ime_call partial_n_seq[] = + { + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uac04", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac04, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + {0}, + }; + struct ime_call partial_na_seq[] = + { + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xac00, .lparam = GCS_RESULTSTR}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xac00, .lparam = 0x1}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub098", .result = L"\uac00", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + {0}, + }; + struct ime_call partial_d_seq[] = + { + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub09f", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb09f, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + {0}, + }; + struct ime_call partial_da_seq[] = + { + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb098, .lparam = GCS_RESULTSTR}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb098, .lparam = 0x1}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\ub2e4", .result = L"\ub098", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + {0}, + }; + struct ime_call partial_return_seq[] = + { + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\ub2e4", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xb2e4, .lparam = GCS_RESULTSTR}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0xb2e4, .lparam = 0x1}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_KEYDOWN, .wparam = 0xd, .lparam = 0x1c0001}}, + {0}, + }; + + INPUTCONTEXT *ctx; + HWND hwnd; + HIMC himc; + UINT i; + + /* this test doesn't work on Win32 / WoW64 */ + if (sizeof(void *) == 4 || default_hkl != (HKL)0x04120412 /* MS Korean IME */) + { + skip( "Got hkl %p, skipping Korean IME-specific test\n", default_hkl ); + process_messages(); + return; + } + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + flush_events(); + + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); + ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) ); + ok_ret( 1, ImmSetConversionStatus( himc, IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE, IME_SMODE_PHRASEPREDICT ) ); + flush_events(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + for (i = 0; i < ARRAY_SIZE(complete_seq); i++) complete_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_g_seq); i++) partial_g_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_ga_seq); i++) partial_ga_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_n_seq); i++) partial_n_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_na_seq); i++) partial_na_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_d_seq); i++) partial_d_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_da_seq); i++) partial_da_seq[i].himc = himc; + for (i = 0; i < ARRAY_SIZE(partial_return_seq); i++) partial_return_seq[i].himc = himc; + + keybd_event( 'R', 0x13, 0, 0 ); + flush_events(); + keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'K', 0x25, 0, 0 ); + flush_events(); + keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'S', 0x1f, 0, 0 ); + flush_events(); + keybd_event( 'S', 0x1f, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'K', 0x25, 0, 0 ); + flush_events(); + keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'E', 0x12, 0, 0 ); + flush_events(); + keybd_event( 'E', 0x12, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'K', 0x25, 0, 0 ); + flush_events(); + keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 ); + + keybd_event( VK_RETURN, 0x1c, 0, 0 ); + flush_events(); + keybd_event( VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0 ); + + flush_events(); + todo_wine ok_seq( complete_seq ); + + + /* Korean IME uses ImeProcessKey and posts messages */ + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'R', MAKELONG(1, 0x13), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'R', MAKELONG(1, 0x13) ) ); + ok_seq( empty_sequence ); + process_messages(); + todo_wine ok_seq( partial_g_seq ); + + /* Korean IME doesn't eat WM_KEYUP */ + + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'R', MAKELONG(1, 0xc013), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'R', MAKELONG(1, 0xc013) ) ); + process_messages(); + ok_seq( empty_sequence ); + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'K', MAKELONG(1, 0x25), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'K', MAKELONG(1, 0x25) ) ); + process_messages(); + todo_wine ok_seq( partial_ga_seq ); + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'S', MAKELONG(1, 0x1f), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'S', MAKELONG(1, 0x1f) ) ); + process_messages(); + todo_wine ok_seq( partial_n_seq ); + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'K', MAKELONG(1, 0x25), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'K', MAKELONG(1, 0x25) ) ); + process_messages(); + todo_wine ok_seq( partial_na_seq ); + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'E', MAKELONG(1, 0x12), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'E', MAKELONG(1, 0x12) ) ); + process_messages(); + todo_wine ok_seq( partial_d_seq ); + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, 'K', MAKELONG(1, 0x25), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'K', MAKELONG(1, 0x25) ) ); + process_messages(); + todo_wine ok_seq( partial_da_seq ); + + todo_wine ok_ret( 2, ImmProcessKey( hwnd, default_hkl, VK_RETURN, MAKELONG(1, 0x1c), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, VK_RETURN, MAKELONG(1, 0x1c) ) ); + process_messages(); + todo_wine ok_seq( partial_return_seq ); + + + ok_ret( 1, ImmSetConversionStatus( himc, 0, IME_SMODE_PHRASEPREDICT ) ); + ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -7258,6 +7527,8 @@ START_TEST(imm32) if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); + test_ga_na_da(); + if (init()) { test_ImmNotifyIME(); From 372e3a212729121dab158aa6c1a2f538c9977832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 30 Apr 2023 16:51:44 +0200 Subject: [PATCH 288/758] imm32/tests: Test MS Japanese IME NIHONGO-NO sequence. (cherry picked from commit e6b21cc57703c02387d2ba55c1fa87902e1c560f) --- dlls/imm32/tests/imm32.c | 180 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 03bf91286a5..456c5d88b7e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -550,13 +550,19 @@ static void ok_seq_( const char *file, int line, const struct ime_call *expected } static BOOL check_WM_SHOWWINDOW; +static BOOL ignore_WM_IME_NOTIFY; +static BOOL ignore_WM_IME_REQUEST; static BOOL ignore_message( UINT msg, WPARAM wparam ) { switch (msg) { case WM_IME_NOTIFY: + if (ignore_WM_IME_NOTIFY) return TRUE; return wparam > IMN_PRIVATE; + case WM_IME_REQUEST: + if (ignore_WM_IME_REQUEST) return TRUE; + return FALSE; case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION: case WM_IME_COMPOSITION: @@ -566,7 +572,6 @@ static BOOL ignore_message( UINT msg, WPARAM wparam ) case WM_IME_SELECT: case WM_IME_CHAR: case 0x287: - case WM_IME_REQUEST: case WM_IME_KEYDOWN: case WM_IME_KEYUP: return FALSE; @@ -7459,6 +7464,178 @@ static void test_ga_na_da(void) ime_call_count = 0; } +static void test_nihongo_no(void) +{ + /* These sequences have some additional WM_IME_NOTIFY messages with wparam > IMN_PRIVATE */ + /* Some out-of-order WM_IME_REQUEST and WM_IME_NOTIFY messages are also ignored */ + struct ime_call complete_seq[] = + { + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uff4e", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xff4e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\uff48", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b\uff4e", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b\u3093\uff47", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306b\u307b\u3093\u3054", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306b, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u65e5\u672c\u8a9e", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x65e5, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\u65e5\u672c\u8a9e", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x65e5, .lparam = GCS_RESULTSTR|GCS_RESULTCLAUSE|GCS_RESULTREADSTR|GCS_RESULTREADCLAUSE}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x65e5, .lparam = 0x1}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x672c, .lparam = 0x1}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x8a9e, .lparam = 0x1}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\uff4e", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0xff4e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u306e", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306e, .lparam = GCS_COMPSTR|GCS_COMPCLAUSE|GCS_COMPATTR|GCS_COMPREADSTR|GCS_DELTASTART|GCS_CURSORPOS}, + }, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"\u306e", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x306e, .lparam = GCS_RESULTSTR|GCS_RESULTCLAUSE|GCS_RESULTREADSTR|GCS_RESULTREADCLAUSE}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_CHAR, .wparam = 0x306e, .lparam = 0x1}}, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}}, + {0}, + }; + + INPUTCONTEXT *ctx; + HWND hwnd; + HIMC himc; + UINT i; + + /* this test doesn't work on Win32 / WoW64 */ + if (sizeof(void *) == 4 || default_hkl != (HKL)0x04110411 /* MS Japanese IME */) + { + skip( "Got hkl %p, skipping Japanese IME-specific test\n", default_hkl ); + return; + } + + hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 100, 100, NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + flush_events(); + + himc = ImmCreateContext(); + ok_ne( NULL, himc, HIMC, "%p" ); + ctx = ImmLockIMC( himc ); + ok_ne( NULL, ctx, INPUTCONTEXT *, "%p" ); + ok_eq( default_himc, ImmAssociateContext( hwnd, himc ), HIMC, "%p" ); + ok_ret( 1, ImmSetOpenStatus( himc, TRUE ) ); + ok_ret( 1, ImmSetConversionStatus( himc, IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE, IME_SMODE_PHRASEPREDICT ) ); + flush_events(); + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; + + for (i = 0; i < ARRAY_SIZE(complete_seq); i++) complete_seq[i].himc = himc; + ignore_WM_IME_REQUEST = TRUE; + ignore_WM_IME_NOTIFY = TRUE; + + + keybd_event( 'N', 0x31, 0, 0 ); + flush_events(); + keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'I', 0x17, 0, 0 ); + flush_events(); + keybd_event( 'I', 0x17, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'H', 0x23, 0, 0 ); + flush_events(); + keybd_event( 'H', 0x23, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'O', 0x18, 0, 0 ); + flush_events(); + keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'N', 0x31, 0, 0 ); + flush_events(); + keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'G', 0x22, 0, 0 ); + flush_events(); + keybd_event( 'G', 0x22, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'O', 0x18, 0, 0 ); + flush_events(); + keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 ); + + keybd_event( VK_SPACE, 0x39, 0, 0 ); + flush_events(); + keybd_event( VK_SPACE, 0x39, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'N', 0x31, 0, 0 ); + flush_events(); + keybd_event( 'N', 0x31, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'O', 0x18, 0, 0 ); + flush_events(); + keybd_event( 'O', 0x18, KEYEVENTF_KEYUP, 0 ); + + keybd_event( VK_RETURN, 0x1c, 0, 0 ); + flush_events(); + keybd_event( VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0 ); + + flush_events(); + todo_wine ok_seq( complete_seq ); + + ignore_WM_IME_REQUEST = FALSE; + ignore_WM_IME_NOTIFY = FALSE; + + /* Japanese IME doesn't take input from ImmProcessKey */ + + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'N', MAKELONG(1, 0x31), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYDOWN, 'N', MAKELONG(1, 0x31) ) ); + flush_events(); + ok_ret( 0, ImmProcessKey( hwnd, default_hkl, 'N', MAKELONG(1, 0xc031), 0 ) ); + ok_ret( 0, ImmTranslateMessage( hwnd, WM_KEYUP, 'N', MAKELONG(1, 0xc031) ) ); + flush_events(); + ok_seq( empty_sequence ); + + + ok_ret( 1, ImmSetConversionStatus( himc, 0, IME_SMODE_PHRASEPREDICT ) ); + ok_ret( 1, ImmSetOpenStatus( himc, FALSE ) ); + + ok_ret( 1, ImmUnlockIMC( himc ) ); + ok_ret( 1, ImmDestroyContext( himc ) ); + + ok_ret( 1, DestroyWindow( hwnd ) ); + process_messages(); + + memset( ime_calls, 0, sizeof(ime_calls) ); + ime_call_count = 0; +} + START_TEST(imm32) { default_hkl = GetKeyboardLayout( 0 ); @@ -7528,6 +7705,7 @@ START_TEST(imm32) if (wineime_hkl) ime_cleanup( wineime_hkl, TRUE ); test_ga_na_da(); + test_nihongo_no(); if (init()) { From ce6992a56a4417af41955e15a13a7175cdddad0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 2 May 2023 11:55:46 +0200 Subject: [PATCH 289/758] winemac: Delay ime_set_text until ImeToAsciiEx requests it. (cherry picked from commit 4071f49fb2dd963d6446ac7b86973c83a482cf7f) --- dlls/winemac.drv/event.c | 47 ++++++++++++++++++++++++++++++++-- dlls/winemac.drv/ime.c | 12 ++++++--- dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_main.c | 2 ++ dlls/winemac.drv/unixlib.h | 1 + 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 03c49b34bae..cc8aa4681c6 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -32,6 +32,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(event); WINE_DECLARE_DEBUG_CHANNEL(imm); +struct ime_update +{ + struct ime_set_text_params *comp_params; + DWORD comp_size; + struct ime_set_text_params *result_params; + DWORD result_size; +}; +static struct ime_update ime_update; /* return the name of an Mac event */ static const char *dbgstr_event(int type) @@ -170,7 +178,20 @@ static void macdrv_im_set_text(const macdrv_event *event) if (length) CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), params->text); - macdrv_client_func(client_func_ime_set_text, params, size); + free(ime_update.comp_params); + ime_update.comp_params = NULL; + + if (params->complete) + { + free(ime_update.result_params); + ime_update.result_params = params; + ime_update.result_size = size; + } + else + { + ime_update.comp_params = params; + ime_update.comp_size = size; + } } /*********************************************************************** @@ -179,7 +200,29 @@ static void macdrv_im_set_text(const macdrv_event *event) static void macdrv_sent_text_input(const macdrv_event *event) { TRACE_(imm)("handled: %s\n", event->sent_text_input.handled ? "TRUE" : "FALSE"); - *event->sent_text_input.done = event->sent_text_input.handled ? 1 : -1; + *event->sent_text_input.done = event->sent_text_input.handled || ime_update.result_params ? 1 : -1; +} + + +/*********************************************************************** + * macdrv_ime_get_text_input + */ +NTSTATUS macdrv_ime_get_text_input(void *arg) +{ + if (ime_update.result_params) + { + macdrv_client_func(client_func_ime_set_text, ime_update.result_params, ime_update.result_size); + free(ime_update.result_params); + ime_update.result_params = NULL; + } + if (ime_update.comp_params) + { + macdrv_client_func(client_func_ime_set_text, ime_update.comp_params, ime_update.comp_size); + free(ime_update.comp_params); + ime_update.comp_params = NULL; + } + + return 0; } diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index bca8377d146..616dc82cc70 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -680,10 +680,16 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, return msgs; } - else if ((lpIMC = LockRealIMC(hIMC))) + else { - UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); - UnlockRealIMC(hIMC); + /* trigger the pending client_func_ime_set_text call */ + MACDRV_CALL(ime_get_text_input, NULL); + + if ((lpIMC = LockRealIMC(hIMC))) + { + UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); + UnlockRealIMC(hIMC); + } } return 0; } diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 281d49c1e9a..b77e62f75db 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -277,6 +277,7 @@ extern NTSTATUS macdrv_dnd_have_format(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_release(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_retain(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_ime_process_text_input(void *arg) DECLSPEC_HIDDEN; +extern NTSTATUS macdrv_ime_get_text_input(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_notify_icon(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_client_func(enum macdrv_client_funcs func, const void *params, diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index a4b280283ee..bf03ab6f5fe 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -635,6 +635,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = macdrv_dnd_retain, macdrv_ime_clear, macdrv_ime_process_text_input, + macdrv_ime_get_text_input, macdrv_ime_using_input_method, macdrv_init, macdrv_notify_icon, @@ -761,6 +762,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = macdrv_dnd_retain, macdrv_ime_clear, wow64_ime_process_text_input, + macdrv_ime_get_text_input, macdrv_ime_using_input_method, wow64_init, wow64_notify_icon, diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index ca5115f4982..b3f45291904 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -28,6 +28,7 @@ enum macdrv_funcs unix_dnd_retain, unix_ime_clear, unix_ime_process_text_input, + unix_ime_get_text_input, unix_ime_using_input_method, unix_init, unix_notify_icon, From 042e5017cb242e4f21b344cd43b00358f7bffbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 2 Apr 2023 21:11:20 +0200 Subject: [PATCH 290/758] winemac: Wait for IME input result on the unix side. (cherry picked from commit 3b42967442924fb154ddf8d0c598ae890cfb6133) --- dlls/winemac.drv/ime.c | 10 +++------- dlls/winemac.drv/keyboard.c | 14 ++++++-------- dlls/winemac.drv/macdrv_main.c | 2 -- dlls/winemac.drv/unixlib.h | 1 - 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 616dc82cc70..0abdeaeb680 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -629,7 +629,7 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, LPIMEPRIVATE myPrivate; HWND hwnd; UINT repeat; - int done = 0; + UINT ret; TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC); @@ -661,13 +661,9 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, params.scan = uScanCode; params.repeat = repeat; params.key_state = lpbKeyState; - params.done = &done; - MACDRV_CALL(ime_process_text_input, ¶ms); + ret = MACDRV_CALL(ime_process_text_input, ¶ms); - while (!done) - MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0); - - if (done < 0) + if (!ret) { UINT msgs = 0; UINT msg = (uScanCode & 0x8000) ? WM_KEYUP : WM_KEYDOWN; diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index bb6fb70fd2a..0f45873a831 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -1198,7 +1198,7 @@ NTSTATUS macdrv_ime_process_text_input(void *arg) void *himc = UlongToHandle(params->himc); const BYTE *key_state = params->key_state; unsigned int flags; - int keyc; + int keyc, done = 0; TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", params->vkey, params->scan, params->repeat, himc); @@ -1225,17 +1225,15 @@ NTSTATUS macdrv_ime_process_text_input(void *arg) for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++) if (thread_data->keyc2vkey[keyc] == params->vkey) break; - if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey)) - { - *params->done = -1; - return 0; - } + if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey)) return 0; TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc); macdrv_send_text_input_event(((params->scan & 0x8000) == 0), flags, params->repeat, keyc, - himc, params->done); - return 0; + himc, &done); + while (!done) NtUserMsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0); + + return done > 0; } diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index bf03ab6f5fe..7e91e0ca3e1 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -673,7 +673,6 @@ static NTSTATUS wow64_ime_process_text_input(void *arg) UINT scan; UINT repeat; ULONG key_state; - ULONG done; } *params32 = arg; struct process_text_input_params params; @@ -682,7 +681,6 @@ static NTSTATUS wow64_ime_process_text_input(void *arg) params.scan = params32->scan; params.repeat = params32->repeat; params.key_state = UlongToPtr(params32->key_state); - params.done = UlongToPtr(params32->done); return macdrv_ime_process_text_input(¶ms); } diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index b3f45291904..493e4f6c139 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -69,7 +69,6 @@ struct process_text_input_params UINT scan; UINT repeat; const BYTE *key_state; - int *done; }; /* macdrv_init params */ From f957d20456ba77b3e43d4d43542d9ae000343fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 2 May 2023 13:55:53 +0200 Subject: [PATCH 291/758] winemac: Send IME key input from ImeProcessKey. (cherry picked from commit 39696138a67e39c5a6d429bf1b7ea8632eb35cff) --- dlls/winemac.drv/ime.c | 72 ++++++++++-------------------------------- include/ntuser.h | 1 - 2 files changed, 17 insertions(+), 56 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 0abdeaeb680..dae7da3b8f3 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -470,24 +470,6 @@ static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam UnlockRealIMC(hIMC); } -static BOOL GenerateMessageToTransKey(TRANSMSGLIST *lpTransBuf, UINT *uNumTranMsgs, - UINT msg, WPARAM wParam, LPARAM lParam) -{ - LPTRANSMSG ptr; - - if (*uNumTranMsgs + 1 >= lpTransBuf->uMsgCount) - return FALSE; - - ptr = lpTransBuf->TransMsg + *uNumTranMsgs; - ptr->message = msg; - ptr->wParam = wParam; - ptr->lParam = lParam; - (*uNumTranMsgs)++; - - return TRUE; -} - - static BOOL IME_RemoveFromSelected(HIMC hIMC) { int i; @@ -536,8 +518,14 @@ static void UpdateDataInDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd, BOOL sh BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) { + struct process_text_input_params params = + { + .himc = (UINT_PTR)hIMC, .vkey = LOWORD(vKey), .scan = HIWORD(lKeyData), + .repeat = !!(lKeyData >> 30), .key_state = lpbKeyState, + }; LPINPUTCONTEXT lpIMC; BOOL inIME; + UINT ret; TRACE("hIMC %p vKey 0x%04x lKeyData 0x%08Ix lpbKeyState %p\n", hIMC, vKey, lKeyData, lpbKeyState); @@ -568,14 +556,17 @@ BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lp ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); } - myPrivate->repeat = (lKeyData >> 30) & 0x1; - myPrivate->bInternalState = inIME; ImmUnlockIMCC(lpIMC->hPrivate); } UnlockRealIMC(hIMC); - return inIME; + if (!inIME) return FALSE; + + TRACE( "Processing Mac 0x%04x\n", vKey ); + ret = MACDRV_CALL( ime_process_text_input, ¶ms ); + + return ret != 0; } BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) @@ -612,7 +603,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) myPrivate->bInternalState = FALSE; myPrivate->textfont = NULL; myPrivate->hwndDefault = NULL; - myPrivate->repeat = 0; ImmUnlockIMCC(lpIMC->hPrivate); UnlockRealIMC(hIMC); } @@ -623,13 +613,10 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC) { - struct process_text_input_params params; UINT vkey; LPINPUTCONTEXT lpIMC; LPIMEPRIVATE myPrivate; HWND hwnd; - UINT repeat; - UINT ret; TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC); @@ -651,41 +638,16 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, return 0; } - repeat = myPrivate->repeat; ImmUnlockIMCC(lpIMC->hPrivate); UnlockRealIMC(hIMC); - TRACE("Processing Mac 0x%04x\n", vkey); - params.himc = HandleToULong(hIMC); - params.vkey = uVKey; - params.scan = uScanCode; - params.repeat = repeat; - params.key_state = lpbKeyState; - ret = MACDRV_CALL(ime_process_text_input, ¶ms); - - if (!ret) - { - UINT msgs = 0; - UINT msg = (uScanCode & 0x8000) ? WM_KEYUP : WM_KEYDOWN; - - /* KeyStroke not processed by the IME - * so we need to rebuild the KeyDown message and pass it on to WINE - */ - if (!GenerateMessageToTransKey(lpdwTransKey, &msgs, msg, vkey, MAKELONG(0x0001, uScanCode))) - GenerateIMEMessage(hIMC, msg, vkey, MAKELONG(0x0001, uScanCode)); + /* trigger the pending client_func_ime_set_text call */ + MACDRV_CALL(ime_get_text_input, NULL); - return msgs; - } - else + if ((lpIMC = LockRealIMC(hIMC))) { - /* trigger the pending client_func_ime_set_text call */ - MACDRV_CALL(ime_get_text_input, NULL); - - if ((lpIMC = LockRealIMC(hIMC))) - { - UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); - UnlockRealIMC(hIMC); - } + UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); + UnlockRealIMC(hIMC); } return 0; } diff --git a/include/ntuser.h b/include/ntuser.h index 67f6cd54837..ee386c143be 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -497,7 +497,6 @@ typedef struct ime_private BOOL bInternalState; HFONT textfont; HWND hwndDefault; - UINT repeat; } IMEPRIVATE, *LPIMEPRIVATE; #define WM_SYSTIMER 0x0118 From 1e7e42e18c6ec257c99eae28b15464360c97de44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 17:15:39 +0200 Subject: [PATCH 292/758] imm32/tests: Mark some tests as broken by prior SetForegroundWindow call. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54760 (cherry picked from commit c11ed566cb762850f2fe84e68025c8f9071b2244) --- dlls/imm32/tests/imm32.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 456c5d88b7e..4bdb6be4a31 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -5012,7 +5012,7 @@ static void test_ImmActivateLayout(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW}, - .todo = TRUE, + .todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */ }, { .hkl = expect_ime, .himc = default_himc, @@ -5046,7 +5046,7 @@ static void test_ImmActivateLayout(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW}, - .todo = TRUE, + .todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */ }, { .hkl = expect_ime, .himc = default_himc, @@ -5203,7 +5203,7 @@ static void test_ImmCreateInputContext(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_OPENSTATUSWINDOW}, - .todo = TRUE, + .todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */ }, {0}, }; @@ -5258,7 +5258,7 @@ static void test_ImmCreateInputContext(void) { .hkl = expect_ime, .himc = default_himc, .func = MSG_IME_UI, .message = {.msg = WM_IME_NOTIFY, .wparam = IMN_CLOSESTATUSWINDOW}, - .todo = TRUE, + .todo = TRUE, .broken = TRUE, /* broken after SetForegroundWindow(GetDesktopWindow()) as in d3d8:device */ }, { .hkl = expect_ime, .himc = default_himc, From 1978f8259eee5e78eb9dcffc2fc19b6cb9cc1679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 10:47:33 +0200 Subject: [PATCH 293/758] winex11: Use a helper to change internal composition status. (cherry picked from commit f3696e6a9c0fbc05a13e63af073dd834784b3bca) --- dlls/winex11.drv/ime.c | 76 +++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 55485bfbfcf..bfca49ab22b 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -453,6 +453,25 @@ static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, UnlockRealIMC(hIMC); } +static void ime_set_composition_status( HIMC himc, BOOL composition ) +{ + struct ime_private *priv; + INPUTCONTEXT *ctx; + UINT msg = 0; + + if (!(ctx = ImmLockIMC( himc ))) return; + if ((priv = ImmLockIMCC( ctx->hPrivate ))) + { + if (!priv->bInComposition && composition) msg = WM_IME_STARTCOMPOSITION; + else if (priv->bInComposition && !composition) msg = WM_IME_ENDCOMPOSITION; + priv->bInComposition = composition; + ImmUnlockIMCC( ctx->hPrivate ); + } + ImmUnlockIMC( himc ); + + if (msg) GenerateIMEMessage( himc, msg, 0, 0 ); +} + static BOOL IME_RemoveFromSelected(HIMC hIMC) { int i; @@ -580,16 +599,8 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) X11DRV_CALL( xim_preedit_state, &preedit_params ); if (!lpIMC->fOpen) { - LPIMEPRIVATE myPrivate; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (myPrivate->bInComposition) - { - X11DRV_CALL( xim_reset, lpIMC->hWnd ); - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - myPrivate->bInComposition = FALSE; - } - ImmUnlockIMCC(lpIMC->hPrivate); + X11DRV_CALL( xim_reset, lpIMC->hWnd ); + ime_set_composition_status( hIMC, FALSE ); } break; @@ -602,7 +613,6 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) case CPS_COMPLETE: { HIMCC newCompStr; - LPIMEPRIVATE myPrivate; WCHAR *str; UINT len; @@ -614,7 +624,6 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; - myPrivate = ImmLockIMCC(lpIMC->hPrivate); if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) { WCHAR param = str[0]; @@ -631,15 +640,10 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param, GCS_RESULTSTR|GCS_RESULTCLAUSE); - - GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION, 0, 0); free( str ); } - else if (myPrivate->bInComposition) - GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION, 0, 0); - myPrivate->bInComposition = FALSE; - ImmUnlockIMCC(lpIMC->hPrivate); + ime_set_composition_status( hIMC, FALSE ); bRet = TRUE; } @@ -648,8 +652,6 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) case CPS_REVERT: FIXME("CPS_REVERT\n"); break; case CPS_CANCEL: { - LPIMEPRIVATE myPrivate; - TRACE("CPS_CANCEL\n"); X11DRV_CALL( xim_reset, lpIMC->hWnd ); @@ -658,13 +660,7 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = ImeCreateBlankCompStr(); - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (myPrivate->bInComposition) - { - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - myPrivate->bInComposition = FALSE; - } - ImmUnlockIMCC(lpIMC->hPrivate); + ime_set_composition_status( hIMC, FALSE ); bRet = TRUE; } break; @@ -685,7 +681,6 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, LPINPUTCONTEXT lpIMC; DWORD flags = 0; WCHAR wParam = 0; - LPIMEPRIVATE myPrivate; TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); @@ -710,17 +705,11 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, if (lpIMC == NULL) return FALSE; - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (dwIndex == SCS_SETSTR) { HIMCC newCompStr; - if (!myPrivate->bInComposition) - { - GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0); - myPrivate->bInComposition = TRUE; - } + ime_set_composition_status( hIMC, TRUE ); /* clear existing result */ newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); @@ -768,30 +757,25 @@ NTSTATUS x11drv_ime_set_composition_status( UINT open ) { HIMC imc; LPINPUTCONTEXT lpIMC; - LPIMEPRIVATE myPrivate; imc = RealIMC(FROM_X11); lpIMC = ImmLockIMC(imc); if (lpIMC == NULL) return 0; - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - - if (open && !myPrivate->bInComposition) - { - GenerateIMEMessage(imc, WM_IME_STARTCOMPOSITION, 0, 0); - } - else if (!open && myPrivate->bInComposition) + if (!open) { + struct ime_private *myPrivate = ImmLockIMCC(lpIMC->hPrivate); ShowWindow(myPrivate->hwndDefault, SW_HIDE); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = ImeCreateBlankCompStr(); - GenerateIMEMessage(imc, WM_IME_ENDCOMPOSITION, 0, 0); + ImmUnlockIMCC(lpIMC->hPrivate); } - myPrivate->bInComposition = open; - ImmUnlockIMCC(lpIMC->hPrivate); ImmUnlockIMC(imc); + + ime_set_composition_status( imc, open ); + return 0; } From 1d716b27bcdf55d79e0967f3eeb947bb72bbf6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 10:53:14 +0200 Subject: [PATCH 294/758] winex11: Clear the composition string when input context is closed. (cherry picked from commit d9fc3eab9a4ae39c94fa484d65679de22d02e75a) --- dlls/winex11.drv/ime.c | 49 ++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index bfca49ab22b..ece40f0dfc2 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -73,6 +73,20 @@ static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT * return text; } +static void input_context_reset_comp_str( INPUTCONTEXT *ctx ) +{ + COMPOSITIONSTRING *compstr; + + if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) + WARN( "Failed to lock input context composition string\n" ); + else + { + memset( compstr, 0, sizeof(*compstr) ); + compstr->dwSize = sizeof(*compstr); + ImmUnlockIMCC( ctx->hCompStr ); + } +} + static HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_X11) @@ -107,18 +121,6 @@ static BOOL UnlockRealIMC(HIMC hIMC) return FALSE; } -static HIMCC ImeCreateBlankCompStr(void) -{ - HIMCC rc; - LPCOMPOSITIONSTRING ptr; - rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); - ptr = ImmLockIMCC(rc); - memset(ptr,0,sizeof(COMPOSITIONSTRING)); - ptr->dwSize = sizeof(COMPOSITIONSTRING); - ImmUnlockIMCC(rc); - return rc; -} - static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset, LPBYTE target, LPBYTE source, DWORD* lenParam, DWORD* offsetParam, BOOL wchars ) @@ -591,8 +593,6 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) } break; case IMC_SETOPENSTATUS: - TRACE("IMC_SETOPENSTATUS\n"); - bRet = TRUE; preedit_params.hwnd = lpIMC->hWnd; preedit_params.open = lpIMC->fOpen; @@ -600,9 +600,9 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) if (!lpIMC->fOpen) { X11DRV_CALL( xim_reset, lpIMC->hWnd ); + input_context_reset_comp_str( lpIMC ); ime_set_composition_status( hIMC, FALSE ); } - break; default: FIXME("Unknown\n"); break; } @@ -643,27 +643,17 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) free( str ); } - ime_set_composition_status( hIMC, FALSE ); - + ImmSetOpenStatus( hIMC, FALSE ); bRet = TRUE; } break; case CPS_CONVERT: FIXME("CPS_CONVERT\n"); break; case CPS_REVERT: FIXME("CPS_REVERT\n"); break; case CPS_CANCEL: - { - TRACE("CPS_CANCEL\n"); - - X11DRV_CALL( xim_reset, lpIMC->hWnd ); - - if (lpIMC->hCompStr) - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = ImeCreateBlankCompStr(); - + input_context_reset_comp_str( lpIMC ); ime_set_composition_status( hIMC, FALSE ); bRet = TRUE; - } - break; + break; default: FIXME("Unknown\n"); break; } break; @@ -767,8 +757,7 @@ NTSTATUS x11drv_ime_set_composition_status( UINT open ) { struct ime_private *myPrivate = ImmLockIMCC(lpIMC->hPrivate); ShowWindow(myPrivate->hwndDefault, SW_HIDE); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = ImeCreateBlankCompStr(); + input_context_reset_comp_str( lpIMC ); ImmUnlockIMCC(lpIMC->hPrivate); } From 3aafd19ac991aa33202f5b6826275a39b2edc883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 13:04:26 +0200 Subject: [PATCH 295/758] winex11: Simplify NotifyIME with NI_COMPOSITIONSTR / CPS_COMPLETE. (cherry picked from commit e2674379c57e202aa6b72e94421284ef41a134e9) --- dlls/winex11.drv/ime.c | 67 ++++++++++++------------------------------ 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index ece40f0dfc2..900a6a3e5d3 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -52,27 +52,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); static HIMC *hSelectedFrom = NULL; static INT hSelectedCount = 0; -static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) -{ - COMPOSITIONSTRING *string; - WCHAR *text = NULL; - UINT len, off; - - if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL; - len = result ? string->dwResultStrLen : string->dwCompStrLen; - off = result ? string->dwResultStrOffset : string->dwCompStrOffset; - - if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) ))) - { - memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) ); - text[len] = 0; - *length = len; - } - - ImmUnlockIMCC( ctx->hCompStr ); - return text; -} - static void input_context_reset_comp_str( INPUTCONTEXT *ctx ) { COMPOSITIONSTRING *compstr; @@ -612,35 +591,27 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) { case CPS_COMPLETE: { - HIMCC newCompStr; - WCHAR *str; - UINT len; - - TRACE("CPS_COMPLETE\n"); - - /* clear existing result */ - newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); - - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; + COMPOSITIONSTRING *compstr; - if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) + if (!(compstr = ImmLockIMCC( lpIMC->hCompStr ))) + WARN( "Failed to lock input context composition string\n" ); + else { - WCHAR param = str[0]; - - newCompStr = updateResultStr( lpIMC->hCompStr, str, len ); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, - GCS_COMPSTR); - - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param, - GCS_RESULTSTR|GCS_RESULTCLAUSE); - free( str ); + WCHAR wchr = *(WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset); + COMPOSITIONSTRING tmp = *compstr; + UINT flags = 0; + + memset( compstr, 0, sizeof(*compstr) ); + compstr->dwSize = tmp.dwSize; + compstr->dwResultStrLen = tmp.dwCompStrLen; + compstr->dwResultStrOffset = tmp.dwCompStrOffset; + compstr->dwResultClauseLen = tmp.dwCompClauseLen; + compstr->dwResultClauseOffset = tmp.dwCompClauseOffset; + ImmUnlockIMCC( lpIMC->hCompStr ); + + if (tmp.dwCompStrLen) flags |= GCS_RESULTSTR; + if (tmp.dwCompClauseLen) flags |= GCS_RESULTCLAUSE; + if (flags) GenerateIMEMessage( hIMC, WM_IME_COMPOSITION, wchr, flags ); } ImmSetOpenStatus( hIMC, FALSE ); From bc742b664144fb34ff20a807c02c5db9fef97d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 13:04:52 +0200 Subject: [PATCH 296/758] win32u: Introduce new NtUserNotifyIMEStatus syscall. (cherry picked from commit 56d0b870b6b443accde891b31251b5cbd7acf6b6) --- dlls/win32u/driver.c | 11 +++++++++ dlls/win32u/imm.c | 8 +++++++ dlls/win32u/syscall.c | 1 + dlls/win32u/win32u.spec | 2 +- dlls/winex11.drv/ime.c | 8 ++----- dlls/winex11.drv/init.c | 1 + dlls/winex11.drv/unixlib.h | 2 -- dlls/winex11.drv/x11drv.h | 3 +-- dlls/winex11.drv/x11drv_main.c | 18 --------------- dlls/winex11.drv/xim.c | 42 +++++++++++----------------------- dlls/wow64win/syscall.h | 1 + dlls/wow64win/user.c | 9 ++++++++ include/ntuser.h | 1 + include/wine/gdi_driver.h | 2 ++ 14 files changed, 51 insertions(+), 58 deletions(-) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 5bae77da5f4..f0055b68d1e 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -716,6 +716,10 @@ static SHORT nulldrv_VkKeyScanEx( WCHAR ch, HKL layout ) return -256; /* use default implementation */ } +static void nulldrv_NotifyIMEStatus( HWND hwnd, UINT status ) +{ +} + static LRESULT nulldrv_DesktopWindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { return default_window_proc( hwnd, msg, wparam, lparam, FALSE ); @@ -1070,6 +1074,11 @@ static SHORT loaderdrv_VkKeyScanEx( WCHAR ch, HKL layout ) return load_driver()->pVkKeyScanEx( ch, layout ); } +static void loaderdrv_NotifyIMEStatus( HWND hwnd, UINT status ) +{ + return load_driver()->pNotifyIMEStatus( hwnd, status ); +} + static LONG loaderdrv_ChangeDisplaySettings( LPDEVMODEW displays, LPCWSTR primary_name, HWND hwnd, DWORD flags, LPVOID lparam ) { @@ -1176,6 +1185,7 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_ToUnicodeEx, loaderdrv_UnregisterHotKey, loaderdrv_VkKeyScanEx, + loaderdrv_NotifyIMEStatus, /* cursor/icon functions */ nulldrv_DestroyCursorIcon, loaderdrv_SetCursor, @@ -1255,6 +1265,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(ToUnicodeEx); SET_USER_FUNC(UnregisterHotKey); SET_USER_FUNC(VkKeyScanEx); + SET_USER_FUNC(NotifyIMEStatus); SET_USER_FUNC(DestroyCursorIcon); SET_USER_FUNC(SetCursor); SET_USER_FUNC(GetCursorPos); diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index 7dee4912e27..328a4a3eddf 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -421,6 +421,14 @@ NTSTATUS WINAPI NtUserBuildHimcList( UINT thread_id, UINT count, HIMC *buffer, U return STATUS_SUCCESS; } +/***************************************************************************** + * NtUserNotifyIMEStatus (win32u.@) + */ +void WINAPI NtUserNotifyIMEStatus( HWND hwnd, UINT status ) +{ + user_driver->pNotifyIMEStatus( hwnd, status ); +} + BOOL WINAPI DECLSPEC_HIDDEN ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM key_data, DWORD unknown ) { struct imm_process_key_params params = diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c index 2e39a4f62cc..3581bc4c27d 100644 --- a/dlls/win32u/syscall.c +++ b/dlls/win32u/syscall.c @@ -235,6 +235,7 @@ static void * const syscalls[] = NtUserMessageCall, NtUserMoveWindow, NtUserMsgWaitForMultipleObjectsEx, + NtUserNotifyIMEStatus, NtUserNotifyWinEvent, NtUserOpenClipboard, NtUserOpenDesktop, diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec index 764eb38d862..7b0629d9d65 100644 --- a/dlls/win32u/win32u.spec +++ b/dlls/win32u/win32u.spec @@ -1087,7 +1087,7 @@ @ stdcall -syscall NtUserMoveWindow(long long long long long long) @ stdcall -syscall NtUserMsgWaitForMultipleObjectsEx(long ptr long long long) @ stub NtUserNavigateFocus -@ stub NtUserNotifyIMEStatus +@ stdcall -syscall NtUserNotifyIMEStatus(long long) @ stub NtUserNotifyProcessCreate @ stdcall -syscall NtUserNotifyWinEvent(long long long long) @ stdcall -syscall NtUserOpenClipboard(long long) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 900a6a3e5d3..cbd8908a12d 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -531,7 +531,6 @@ UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) { - struct xim_preedit_state_params preedit_params; BOOL bRet = FALSE; LPINPUTCONTEXT lpIMC; @@ -572,16 +571,13 @@ BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) } break; case IMC_SETOPENSTATUS: - bRet = TRUE; - preedit_params.hwnd = lpIMC->hWnd; - preedit_params.open = lpIMC->fOpen; - X11DRV_CALL( xim_preedit_state, &preedit_params ); if (!lpIMC->fOpen) { - X11DRV_CALL( xim_reset, lpIMC->hWnd ); input_context_reset_comp_str( lpIMC ); ime_set_composition_status( hIMC, FALSE ); } + NtUserNotifyIMEStatus( lpIMC->hWnd, lpIMC->fOpen ); + bRet = TRUE; break; default: FIXME("Unknown\n"); break; } diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index ef11461ea67..bc4627d0b1c 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -400,6 +400,7 @@ static const struct user_driver_funcs x11drv_funcs = .pMapVirtualKeyEx = X11DRV_MapVirtualKeyEx, .pToUnicodeEx = X11DRV_ToUnicodeEx, .pVkKeyScanEx = X11DRV_VkKeyScanEx, + .pNotifyIMEStatus = X11DRV_NotifyIMEStatus, .pDestroyCursorIcon = X11DRV_DestroyCursorIcon, .pSetCursor = X11DRV_SetCursor, .pGetCursorPos = X11DRV_GetCursorPos, diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 7dc1d9f0ca7..20279bdb2ac 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -31,8 +31,6 @@ enum x11drv_funcs unix_tablet_get_packet, unix_tablet_info, unix_tablet_load_info, - unix_xim_preedit_state, - unix_xim_reset, unix_funcs_count, }; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 8a035f3a5c1..3bfe28f39a4 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -211,6 +211,7 @@ extern UINT X11DRV_MapVirtualKeyEx( UINT code, UINT map_type, HKL hkl ) DECLSPEC extern INT X11DRV_ToUnicodeEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState, LPWSTR bufW, int bufW_size, UINT flags, HKL hkl ) DECLSPEC_HIDDEN; extern SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) DECLSPEC_HIDDEN; +extern void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status ) DECLSPEC_HIDDEN; extern void X11DRV_DestroyCursorIcon( HCURSOR handle ) DECLSPEC_HIDDEN; extern void X11DRV_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; extern BOOL X11DRV_SetCursorPos( INT x, INT y ) DECLSPEC_HIDDEN; @@ -902,8 +903,6 @@ extern NTSTATUS x11drv_tablet_attach_queue( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_tablet_get_packet( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_tablet_load_info( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_tablet_info( void *arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_xim_preedit_state( void *arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_xim_reset( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_client_func( enum x11drv_client_funcs func, const void *params, ULONG size ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 3ac5d0d167c..0366ed51d8d 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -1486,8 +1486,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = x11drv_tablet_get_packet, x11drv_tablet_info, x11drv_tablet_load_info, - x11drv_xim_preedit_state, - x11drv_xim_reset, }; @@ -1564,20 +1562,6 @@ static NTSTATUS x11drv_wow64_tablet_info( void *arg ) return x11drv_tablet_info( ¶ms ); } -static NTSTATUS x11drv_wow64_xim_preedit_state( void *arg ) -{ - struct - { - ULONG hwnd; - BOOL open; - } *params32 = arg; - struct xim_preedit_state_params params; - - params.hwnd = UlongToHandle( params32->hwnd ); - params.open = params32->open; - return x11drv_xim_preedit_state( ¶ms ); -} - const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { x11drv_create_desktop, @@ -1590,8 +1574,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = x11drv_wow64_tablet_get_packet, x11drv_wow64_tablet_info, x11drv_tablet_load_info, - x11drv_wow64_xim_preedit_state, - x11drv_xim_reset, }; C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count ); diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 3601cb8c680..b7ab69029b9 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -260,42 +260,26 @@ static int xic_status_draw( XIC xic, XPointer user, XPointer arg ) return 0; } -NTSTATUS x11drv_xim_reset( void *hwnd ) -{ - XIC ic = X11DRV_get_ic(hwnd); - if (ic) - { - char* leftover; - TRACE("Forcing Reset %p\n",ic); - leftover = XmbResetIC(ic); - XFree(leftover); - } - return 0; -} - -NTSTATUS x11drv_xim_preedit_state( void *arg ) +/*********************************************************************** + * NotifyIMEStatus (X11DRV.@) + */ +void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status ) { - struct xim_preedit_state_params *params = arg; - XIC ic; - XIMPreeditState state; + XIMPreeditState state = status ? XIMPreeditEnable : XIMPreeditDisable; XVaNestedList attr; + XIC xic; - ic = X11DRV_get_ic( params->hwnd ); - if (!ic) - return 0; + TRACE( "hwnd %p, status %#x\n", hwnd, status ); - if (params->open) - state = XIMPreeditEnable; - else - state = XIMPreeditDisable; + if (!(xic = X11DRV_get_ic( hwnd ))) return; - attr = XVaCreateNestedList(0, XNPreeditState, state, NULL); - if (attr != NULL) + if ((attr = XVaCreateNestedList( 0, XNPreeditState, state, NULL ))) { - XSetICValues(ic, XNPreeditAttributes, attr, NULL); - XFree(attr); + XSetICValues( xic, XNPreeditAttributes, attr, NULL ); + XFree( attr ); } - return 0; + + if (!status) XFree( XmbResetIC( xic ) ); } /*********************************************************************** diff --git a/dlls/wow64win/syscall.h b/dlls/wow64win/syscall.h index c9dc459709e..3ff6ecf7c0e 100644 --- a/dlls/wow64win/syscall.h +++ b/dlls/wow64win/syscall.h @@ -220,6 +220,7 @@ SYSCALL_ENTRY( NtUserMessageCall ) \ SYSCALL_ENTRY( NtUserMoveWindow ) \ SYSCALL_ENTRY( NtUserMsgWaitForMultipleObjectsEx ) \ + SYSCALL_ENTRY( NtUserNotifyIMEStatus ) \ SYSCALL_ENTRY( NtUserNotifyWinEvent ) \ SYSCALL_ENTRY( NtUserOpenClipboard ) \ SYSCALL_ENTRY( NtUserOpenDesktop ) \ diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c index 458f5cd0788..f0b57f7c886 100644 --- a/dlls/wow64win/user.c +++ b/dlls/wow64win/user.c @@ -3164,6 +3164,15 @@ NTSTATUS WINAPI wow64_NtUserMsgWaitForMultipleObjectsEx( UINT *args ) return NtUserMsgWaitForMultipleObjectsEx( count, handles, timeout, mask, flags ); } +NTSTATUS WINAPI wow64_NtUserNotifyIMEStatus( UINT *args ) +{ + HWND hwnd = get_handle( &args ); + ULONG status = get_ulong( &args ); + + NtUserNotifyIMEStatus( hwnd, status ); + return 0; +} + NTSTATUS WINAPI wow64_NtUserNotifyWinEvent( UINT *args ) { DWORD event = get_ulong( &args ); diff --git a/include/ntuser.h b/include/ntuser.h index ee386c143be..198f7889020 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -819,6 +819,7 @@ LRESULT WINAPI NtUserMessageCall( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpa BOOL WINAPI NtUserMoveWindow( HWND hwnd, INT x, INT y, INT cx, INT cy, BOOL repaint ); DWORD WINAPI NtUserMsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles, DWORD timeout, DWORD mask, DWORD flags ); +void WINAPI NtUserNotifyIMEStatus( HWND hwnd, UINT status ); void WINAPI NtUserNotifyWinEvent( DWORD event, HWND hwnd, LONG object_id, LONG child_id ); HWINSTA WINAPI NtUserOpenWindowStation( OBJECT_ATTRIBUTES *attr, ACCESS_MASK access ); BOOL WINAPI NtUserOpenClipboard( HWND hwnd, ULONG unk ); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index f556da495e9..71227a2bf23 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -286,6 +286,8 @@ struct user_driver_funcs INT (*pToUnicodeEx)(UINT,UINT,const BYTE *,LPWSTR,int,UINT,HKL); void (*pUnregisterHotKey)(HWND, UINT, UINT); SHORT (*pVkKeyScanEx)(WCHAR, HKL); + /* IME functions */ + void (*pNotifyIMEStatus)(HWND,UINT); /* cursor/icon functions */ void (*pDestroyCursorIcon)(HCURSOR); void (*pSetCursor)(HCURSOR); From 45c15405e03ed8e94017f1d9d33d8e1ea2f9ec22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 13:06:17 +0200 Subject: [PATCH 297/758] winex11: Move NotifyIME to the default IME implementation. (cherry picked from commit 800af36d9305fe24099805fb9100e03a77925c62) --- dlls/imm32/ime.c | 140 +++++++++++++++++++++++++++++- dlls/winex11.drv/ime.c | 102 ---------------------- dlls/winex11.drv/winex11.drv.spec | 1 - 3 files changed, 137 insertions(+), 106 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 3c4550c3cd9..44b1173b1db 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -83,6 +83,20 @@ static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT * return text; } +static void input_context_reset_comp_str( INPUTCONTEXT *ctx ) +{ + COMPOSITIONSTRING *compstr; + + if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) + WARN( "Failed to lock input context composition string\n" ); + else + { + memset( compstr, 0, sizeof(*compstr) ); + compstr->dwSize = sizeof(*compstr); + ImmUnlockIMCC( ctx->hCompStr ); + } +} + static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) { struct ime_private *priv; @@ -93,6 +107,47 @@ static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) return font; } +static void ime_send_message( HIMC himc, UINT message, WPARAM wparam, LPARAM lparam ) +{ + INPUTCONTEXT *ctx; + TRANSMSG *msgs; + HIMCC himcc; + + if (!(ctx = ImmLockIMC( himc ))) return; + if (!(himcc = ImmReSizeIMCC( ctx->hMsgBuf, (ctx->dwNumMsgBuf + 1) * sizeof(*msgs) ))) + WARN( "Failed to resize input context message buffer\n" ); + else if (!(msgs = ImmLockIMCC( (ctx->hMsgBuf = himcc) ))) + WARN( "Failed to lock input context message buffer\n" ); + else + { + TRANSMSG msg = {.message = message, .wParam = wparam, .lParam = lparam}; + msgs[ctx->dwNumMsgBuf++] = msg; + ImmUnlockIMCC( ctx->hMsgBuf ); + } + + ImmUnlockIMC( himc ); + ImmGenerateMessage( himc ); +} + +static void ime_set_composition_status( HIMC himc, BOOL composition ) +{ + struct ime_private *priv; + INPUTCONTEXT *ctx; + UINT msg = 0; + + if (!(ctx = ImmLockIMC( himc ))) return; + if ((priv = ImmLockIMCC( ctx->hPrivate ))) + { + if (!priv->bInComposition && composition) msg = WM_IME_STARTCOMPOSITION; + else if (priv->bInComposition && !composition) msg = WM_IME_ENDCOMPOSITION; + priv->bInComposition = composition; + ImmUnlockIMCC( ctx->hPrivate ); + } + ImmUnlockIMC( himc ); + + if (msg) ime_send_message( himc, msg, 0, 0 ); +} + static void ime_ui_paint( HIMC himc, HWND hwnd ) { PAINTSTRUCT ps; @@ -390,9 +445,88 @@ BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, D BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) { - FIXME( "himc %p, action %lu, index %lu, value %lu stub!\n", himc, action, index, value ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + struct ime_private *priv; + INPUTCONTEXT *ctx; + + TRACE( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value ); + + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + + switch (action) + { + case NI_CONTEXTUPDATED: + switch (value) + { + case IMC_SETCOMPOSITIONFONT: + if ((priv = ImmLockIMCC( ctx->hPrivate ))) + { + if (priv->textfont) DeleteObject( priv->textfont ); + priv->textfont = CreateFontIndirectW( &ctx->lfFont.W ); + ImmUnlockIMCC( ctx->hPrivate ); + } + break; + case IMC_SETOPENSTATUS: + if (!ctx->fOpen) + { + input_context_reset_comp_str( ctx ); + ime_set_composition_status( himc, FALSE ); + } + NtUserNotifyIMEStatus( ctx->hWnd, ctx->fOpen ); + break; + default: + FIXME( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value ); + break; + } + break; + + case NI_COMPOSITIONSTR: + switch (index) + { + case CPS_COMPLETE: + { + COMPOSITIONSTRING *compstr; + + if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) + WARN( "Failed to lock input context composition string\n" ); + else + { + WCHAR wchr = *(WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset); + COMPOSITIONSTRING tmp = *compstr; + UINT flags = 0; + + memset( compstr, 0, sizeof(*compstr) ); + compstr->dwSize = tmp.dwSize; + compstr->dwResultStrLen = tmp.dwCompStrLen; + compstr->dwResultStrOffset = tmp.dwCompStrOffset; + compstr->dwResultClauseLen = tmp.dwCompClauseLen; + compstr->dwResultClauseOffset = tmp.dwCompClauseOffset; + ImmUnlockIMCC( ctx->hCompStr ); + + if (tmp.dwCompStrLen) flags |= GCS_RESULTSTR; + if (tmp.dwCompClauseLen) flags |= GCS_RESULTCLAUSE; + if (flags) ime_send_message( himc, WM_IME_COMPOSITION, wchr, flags ); + } + + ImmSetOpenStatus( himc, FALSE ); + break; + } + case CPS_CANCEL: + input_context_reset_comp_str( ctx ); + ImmSetOpenStatus( himc, FALSE ); + break; + default: + FIXME( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value ); + break; + } + break; + + default: + FIXME( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value ); + break; + } + + ImmUnlockIMC( himc ); + return TRUE; } LRESULT WINAPI ImeEscape( HIMC himc, UINT escape, void *data ) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index cbd8908a12d..3cc73c3974c 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -529,108 +529,6 @@ UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, return 0; } -BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) -{ - BOOL bRet = FALSE; - LPINPUTCONTEXT lpIMC; - - TRACE("%p %li %li %li\n",hIMC,dwAction,dwIndex,dwValue); - - lpIMC = LockRealIMC(hIMC); - if (lpIMC == NULL) - return FALSE; - - switch (dwAction) - { - case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break; - case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break; - case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break; - case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break; - case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break; - case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break; - case NI_CONTEXTUPDATED: - switch (dwValue) - { - case IMC_SETCOMPOSITIONWINDOW: FIXME("IMC_SETCOMPOSITIONWINDOW\n"); break; - case IMC_SETCONVERSIONMODE: FIXME("IMC_SETCONVERSIONMODE\n"); break; - case IMC_SETSENTENCEMODE: FIXME("IMC_SETSENTENCEMODE\n"); break; - case IMC_SETCANDIDATEPOS: FIXME("IMC_SETCANDIDATEPOS\n"); break; - case IMC_SETCOMPOSITIONFONT: - { - LPIMEPRIVATE myPrivate; - TRACE("IMC_SETCOMPOSITIONFONT\n"); - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (myPrivate->textfont) - { - DeleteObject(myPrivate->textfont); - myPrivate->textfont = NULL; - } - myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W); - ImmUnlockIMCC(lpIMC->hPrivate); - } - break; - case IMC_SETOPENSTATUS: - if (!lpIMC->fOpen) - { - input_context_reset_comp_str( lpIMC ); - ime_set_composition_status( hIMC, FALSE ); - } - NtUserNotifyIMEStatus( lpIMC->hWnd, lpIMC->fOpen ); - bRet = TRUE; - break; - default: FIXME("Unknown\n"); break; - } - break; - case NI_COMPOSITIONSTR: - switch (dwIndex) - { - case CPS_COMPLETE: - { - COMPOSITIONSTRING *compstr; - - if (!(compstr = ImmLockIMCC( lpIMC->hCompStr ))) - WARN( "Failed to lock input context composition string\n" ); - else - { - WCHAR wchr = *(WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset); - COMPOSITIONSTRING tmp = *compstr; - UINT flags = 0; - - memset( compstr, 0, sizeof(*compstr) ); - compstr->dwSize = tmp.dwSize; - compstr->dwResultStrLen = tmp.dwCompStrLen; - compstr->dwResultStrOffset = tmp.dwCompStrOffset; - compstr->dwResultClauseLen = tmp.dwCompClauseLen; - compstr->dwResultClauseOffset = tmp.dwCompClauseOffset; - ImmUnlockIMCC( lpIMC->hCompStr ); - - if (tmp.dwCompStrLen) flags |= GCS_RESULTSTR; - if (tmp.dwCompClauseLen) flags |= GCS_RESULTCLAUSE; - if (flags) GenerateIMEMessage( hIMC, WM_IME_COMPOSITION, wchr, flags ); - } - - ImmSetOpenStatus( hIMC, FALSE ); - bRet = TRUE; - } - break; - case CPS_CONVERT: FIXME("CPS_CONVERT\n"); break; - case CPS_REVERT: FIXME("CPS_REVERT\n"); break; - case CPS_CANCEL: - input_context_reset_comp_str( lpIMC ); - ime_set_composition_status( hIMC, FALSE ); - bRet = TRUE; - break; - default: FIXME("Unknown\n"); break; - } - break; - default: FIXME("Unknown Message\n"); break; - } - - UnlockRealIMC(hIMC); - return bRet; -} - BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, LPCVOID lpRead, DWORD dwReadLen) diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index a753536eb3f..da502c4832e 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -13,6 +13,5 @@ #IME Interface @ stdcall ImeSelect(long long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) -@ stdcall NotifyIME(long long long long) @ stdcall ImeSetCompositionString(long long ptr long ptr long) @ stdcall ImeProcessKey(long long long ptr) From 6eefbcd96553d239d114bc38fb74f7e95565aecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 11:38:32 +0200 Subject: [PATCH 298/758] winemac: Use the default IME implementation for NotifyIME. (cherry picked from commit 4c9154536737e33bb17e3dafb073dba3292570dd) --- dlls/winemac.drv/event.c | 10 ++ dlls/winemac.drv/gdi.c | 1 + dlls/winemac.drv/ime.c | 154 +----------------------------- dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_main.c | 9 -- dlls/winemac.drv/unixlib.h | 1 - dlls/winemac.drv/winemac.drv.spec | 1 - 7 files changed, 14 insertions(+), 163 deletions(-) diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index cc8aa4681c6..6a695f87e58 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -356,6 +356,16 @@ BOOL query_ime_char_rect(macdrv_query* query) } +/*********************************************************************** + * NotifyIMEStatus (X11DRV.@) + */ +void macdrv_NotifyIMEStatus( HWND hwnd, UINT status ) +{ + TRACE_(imm)( "hwnd %p, status %#x\n", hwnd, status ); + if (!status) macdrv_clear_ime_text(); +} + + /*********************************************************************** * macdrv_query_event * diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index d22532fd3b7..9c594a5fff6 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -302,6 +302,7 @@ static const struct user_driver_funcs macdrv_funcs = .pUpdateClipboard = macdrv_UpdateClipboard, .pUpdateLayeredWindow = macdrv_UpdateLayeredWindow, .pVkKeyScanEx = macdrv_VkKeyScanEx, + .pNotifyIMEStatus = macdrv_NotifyIMEStatus, .pWindowMessage = macdrv_WindowMessage, .pWindowPosChanged = macdrv_WindowPosChanged, .pWindowPosChanging = macdrv_WindowPosChanging, diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index dae7da3b8f3..617533e956f 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -652,156 +652,6 @@ UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, return 0; } -BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) -{ - BOOL bRet = FALSE; - LPINPUTCONTEXT lpIMC; - - TRACE("%p %li %li %li\n", hIMC, dwAction, dwIndex, dwValue); - - lpIMC = LockRealIMC(hIMC); - if (lpIMC == NULL) - return FALSE; - - switch (dwAction) - { - case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break; - case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break; - case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break; - case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break; - case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break; - case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break; - case NI_CONTEXTUPDATED: - switch (dwValue) - { - case IMC_SETCOMPOSITIONWINDOW: FIXME("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONWINDOW\n"); break; - case IMC_SETCONVERSIONMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETCONVERSIONMODE\n"); break; - case IMC_SETSENTENCEMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETSENTENCEMODE\n"); break; - case IMC_SETCANDIDATEPOS: FIXME("NI_CONTEXTUPDATED: IMC_SETCANDIDATEPOS\n"); break; - case IMC_SETCOMPOSITIONFONT: - { - LPIMEPRIVATE myPrivate; - TRACE("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONFONT\n"); - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (myPrivate->textfont) - { - DeleteObject(myPrivate->textfont); - myPrivate->textfont = NULL; - } - myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W); - ImmUnlockIMCC(lpIMC->hPrivate); - } - break; - case IMC_SETOPENSTATUS: - { - LPIMEPRIVATE myPrivate; - TRACE("NI_CONTEXTUPDATED: IMC_SETOPENSTATUS\n"); - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (lpIMC->fOpen != myPrivate->bInternalState && myPrivate->bInComposition) - { - if(lpIMC->fOpen == FALSE) - { - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - myPrivate->bInComposition = FALSE; - } - else - { - GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0); - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, 0); - } - } - myPrivate->bInternalState = lpIMC->fOpen; - bRet = TRUE; - } - break; - default: FIXME("NI_CONTEXTUPDATED: Unknown\n"); break; - } - break; - case NI_COMPOSITIONSTR: - switch (dwIndex) - { - case CPS_COMPLETE: - { - HIMCC newCompStr; - LPIMEPRIVATE myPrivate; - WCHAR *str; - UINT len; - - TRACE("NI_COMPOSITIONSTR: CPS_COMPLETE\n"); - - /* clear existing result */ - newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); - - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if ((str = input_context_get_comp_str( lpIMC, FALSE, &len ))) - { - WCHAR param = str[0]; - DWORD flags = GCS_COMPSTR; - - newCompStr = updateResultStr( lpIMC->hCompStr, str, len ); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, flags); - - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param, - GCS_RESULTSTR | GCS_RESULTCLAUSE); - - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - free( str ); - } - else if (myPrivate->bInComposition) - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - - - myPrivate->bInComposition = FALSE; - ImmUnlockIMCC(lpIMC->hPrivate); - - bRet = TRUE; - } - break; - case CPS_CONVERT: FIXME("NI_COMPOSITIONSTR: CPS_CONVERT\n"); break; - case CPS_REVERT: FIXME("NI_COMPOSITIONSTR: CPS_REVERT\n"); break; - case CPS_CANCEL: - { - LPIMEPRIVATE myPrivate; - - TRACE("NI_COMPOSITIONSTR: CPS_CANCEL\n"); - - MACDRV_CALL(ime_clear, NULL); - if (lpIMC->hCompStr) - ImmDestroyIMCC(lpIMC->hCompStr); - - lpIMC->hCompStr = ImeCreateBlankCompStr(); - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (myPrivate->bInComposition) - { - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - myPrivate->bInComposition = FALSE; - } - ImmUnlockIMCC(lpIMC->hPrivate); - bRet = TRUE; - } - break; - default: FIXME("NI_COMPOSITIONSTR: Unknown\n"); break; - } - break; - default: FIXME("Unknown Message\n"); break; - } - - UnlockRealIMC(hIMC); - return bRet; -} - static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid) { LPINPUTCONTEXT lpIMC; @@ -863,7 +713,7 @@ static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, } else { - NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); + ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); sendMessage = FALSE; } @@ -891,7 +741,7 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DW static void IME_NotifyComplete(void* hIMC) { - NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); + ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); } /* Interfaces to other parts of the Mac driver */ diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index b77e62f75db..71f1215da60 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -167,6 +167,7 @@ extern INT macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyStat LPWSTR bufW, int bufW_size, UINT flags, HKL hkl) DECLSPEC_HIDDEN; extern UINT macdrv_GetKeyboardLayoutList(INT size, HKL *list) DECLSPEC_HIDDEN; extern INT macdrv_GetKeyNameText(LONG lparam, LPWSTR buffer, INT size) DECLSPEC_HIDDEN; +extern void macdrv_NotifyIMEStatus( HWND hwnd, UINT status ) DECLSPEC_HIDDEN; extern BOOL macdrv_SystemParametersInfo(UINT action, UINT int_param, void *ptr_param, UINT flags) DECLSPEC_HIDDEN; extern BOOL macdrv_ProcessEvents(DWORD mask) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 7e91e0ca3e1..9125574e35b 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -605,13 +605,6 @@ NTSTATUS macdrv_client_func(enum macdrv_client_funcs id, const void *params, ULO } -static NTSTATUS macdrv_ime_clear(void *arg) -{ - macdrv_clear_ime_text(); - return 0; -} - - static NTSTATUS macdrv_ime_using_input_method(void *arg) { return macdrv_using_input_method(); @@ -633,7 +626,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = macdrv_dnd_have_format, macdrv_dnd_release, macdrv_dnd_retain, - macdrv_ime_clear, macdrv_ime_process_text_input, macdrv_ime_get_text_input, macdrv_ime_using_input_method, @@ -758,7 +750,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = macdrv_dnd_have_format, macdrv_dnd_release, macdrv_dnd_retain, - macdrv_ime_clear, wow64_ime_process_text_input, macdrv_ime_get_text_input, macdrv_ime_using_input_method, diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 493e4f6c139..1f7840a1546 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -26,7 +26,6 @@ enum macdrv_funcs unix_dnd_have_format, unix_dnd_release, unix_dnd_retain, - unix_ime_clear, unix_ime_process_text_input, unix_ime_get_text_input, unix_ime_using_input_method, diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index debaec8239d..5a9403c3cf5 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -6,4 +6,3 @@ @ stdcall ImeSelect(long long) @ stdcall ImeSetCompositionString(long long ptr long ptr long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) -@ stdcall NotifyIME(long long long long) From 78fba33f1bfc488467ba86f1805dbb8bb1734810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 5 May 2023 14:24:43 +0200 Subject: [PATCH 299/758] winex11: Use the default IME implementation for ImeSetCompositionString. (cherry picked from commit 2c74f4ede1ebb0ac25eb4c1a20ec99c8af4f29e0) --- dlls/imm32/ime.c | 70 +++++++++++++++++++++++++++---- dlls/winex11.drv/ime.c | 70 +------------------------------ dlls/winex11.drv/winex11.drv.spec | 1 - 3 files changed, 63 insertions(+), 78 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 44b1173b1db..3e5f34d6760 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -83,16 +83,51 @@ static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT * return text; } -static void input_context_reset_comp_str( INPUTCONTEXT *ctx ) +static void input_context_set_comp_str( INPUTCONTEXT *ctx, const WCHAR *str, UINT len ) { COMPOSITIONSTRING *compstr; + HIMCC himcc; + UINT size; + BYTE *dst; + + size = sizeof(*compstr); + size += len * sizeof(WCHAR); /* GCS_COMPSTR */ + size += len; /* GCS_COMPSTRATTR */ + size += 2 * sizeof(DWORD); /* GCS_COMPSTRCLAUSE */ - if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) + if (!(himcc = ImmReSizeIMCC( ctx->hCompStr, size ))) + WARN( "Failed to resize input context composition string\n" ); + else if (!(compstr = ImmLockIMCC( (ctx->hCompStr = himcc) ))) WARN( "Failed to lock input context composition string\n" ); else { memset( compstr, 0, sizeof(*compstr) ); compstr->dwSize = sizeof(*compstr); + + if (len) + { + compstr->dwCursorPos = len; + + compstr->dwCompStrLen = len; + compstr->dwCompStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompStrOffset; + memcpy( dst, str, compstr->dwCompStrLen * sizeof(WCHAR) ); + compstr->dwSize += compstr->dwCompStrLen * sizeof(WCHAR); + + compstr->dwCompClauseLen = 2 * sizeof(DWORD); + compstr->dwCompClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwCompStrLen; + compstr->dwSize += compstr->dwCompClauseLen; + + compstr->dwCompAttrLen = compstr->dwCompStrLen; + compstr->dwCompAttrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompAttrOffset; + memset( dst, ATTR_INPUT, compstr->dwCompAttrLen ); + compstr->dwSize += compstr->dwCompAttrLen; + } + ImmUnlockIMCC( ctx->hCompStr ); } } @@ -437,10 +472,29 @@ DWORD WINAPI ImeConversionList( HIMC himc, const WCHAR *source, CANDIDATELIST *d BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, DWORD comp_len, const void *read, DWORD read_len ) { - FIXME( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu stub!\n", - himc, index, comp, comp_len, read, read_len ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + INPUTCONTEXT *ctx; + + FIXME( "himc %p, index %lu, comp %p, comp_len %lu, read %p, read_len %lu semi-stub!\n", + himc, index, comp, comp_len, read, read_len ); + if (read && read_len) FIXME( "Read string unimplemented\n" ); + if (index != SCS_SETSTR && index != SCS_CHANGECLAUSE && index != SCS_CHANGEATTR) return FALSE; + + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + + if (index != SCS_SETSTR) + FIXME( "index %#lx not implemented\n", index ); + else + { + UINT flags = GCS_COMPSTR | GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART | GCS_CURSORPOS; + WCHAR wparam = comp && comp_len >= sizeof(WCHAR) ? *(WCHAR *)comp : 0; + input_context_set_comp_str( ctx, comp, comp_len / sizeof(WCHAR) ); + ime_set_composition_status( himc, TRUE ); + ime_send_message( himc, WM_IME_COMPOSITION, wparam, flags ); + } + + ImmUnlockIMC( himc ); + + return TRUE; } BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) @@ -468,7 +522,7 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) case IMC_SETOPENSTATUS: if (!ctx->fOpen) { - input_context_reset_comp_str( ctx ); + input_context_set_comp_str( ctx, NULL, 0 ); ime_set_composition_status( himc, FALSE ); } NtUserNotifyIMEStatus( ctx->hWnd, ctx->fOpen ); @@ -511,7 +565,7 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) break; } case CPS_CANCEL: - input_context_reset_comp_str( ctx ); + input_context_set_comp_str( ctx, NULL, 0 ); ImmSetOpenStatus( himc, FALSE ); break; default: diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 3cc73c3974c..b46c2d86892 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -529,74 +529,6 @@ UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, return 0; } -BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, - DWORD dwCompLen, LPCVOID lpRead, - DWORD dwReadLen) -{ - LPINPUTCONTEXT lpIMC; - DWORD flags = 0; - WCHAR wParam = 0; - - TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", - hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); - - - if (hIMC != FROM_X11) - FIXME("PROBLEM: This only sets the wine level string\n"); - - /* - * Explanation: - * this sets the composition string in the imm32.dll level - * of the composition buffer. we cannot manipulate the xim level - * buffer, which means that once the xim level buffer changes again - * any call to this function from the application will be lost - */ - - if (lpRead && dwReadLen) - FIXME("Reading string unimplemented\n"); - - lpIMC = LockRealIMC(hIMC); - - if (lpIMC == NULL) - return FALSE; - - if (dwIndex == SCS_SETSTR) - { - HIMCC newCompStr; - - ime_set_composition_status( hIMC, TRUE ); - - /* clear existing result */ - newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - flags = GCS_COMPSTR; - - if (dwCompLen && lpComp) - { - newCompStr = updateCompStr(lpIMC->hCompStr, (LPCWSTR)lpComp, dwCompLen / sizeof(WCHAR)); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - wParam = ((const WCHAR*)lpComp)[0]; - flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART; - } - else - { - newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - } - } - - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags); - ImmUnlockIMCC(lpIMC->hPrivate); - UnlockRealIMC(hIMC); - - return TRUE; -} - /* Interfaces to XIM and other parts of winex11drv */ NTSTATUS x11drv_ime_set_open_status( UINT open ) @@ -693,7 +625,7 @@ NTSTATUS x11drv_ime_update_association( UINT arg ) NTSTATUS WINAPI x11drv_ime_set_composition_string( void *param, ULONG size ) { - return ImeSetCompositionString(FROM_X11, SCS_SETSTR, param, size, NULL, 0); + return ImmSetCompositionStringW( RealIMC(FROM_X11), SCS_SETSTR, param, size, NULL, 0 ); } NTSTATUS WINAPI x11drv_ime_set_result( void *params, ULONG len ) diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index da502c4832e..64154d709a2 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -13,5 +13,4 @@ #IME Interface @ stdcall ImeSelect(long long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) -@ stdcall ImeSetCompositionString(long long ptr long ptr long) @ stdcall ImeProcessKey(long long long ptr) From 1470197dac74ff04db10e0df05b130309a3911f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 13:44:43 +0200 Subject: [PATCH 300/758] winemac: Use the default IME implementation for ImeSetCompositionString. (cherry picked from commit ab98b13480049b3cdb0e842adcde28b8ee9e265e) --- dlls/winemac.drv/ime.c | 11 ----------- dlls/winemac.drv/winemac.drv.spec | 1 - 2 files changed, 12 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 617533e956f..65a79df2570 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -728,17 +728,6 @@ static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, return TRUE; } -BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, - LPCVOID lpRead, DWORD dwReadLen) -{ - TRACE("(%p, %ld, %p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); - - if (lpRead && dwReadLen) - FIXME("Reading string unimplemented\n"); - - return IME_SetCompositionString(hIMC, dwIndex, lpComp, dwCompLen, 0, FALSE); -} - static void IME_NotifyComplete(void* hIMC) { ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 5a9403c3cf5..aca1c58cf13 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -4,5 +4,4 @@ # IME @ stdcall ImeProcessKey(long long long ptr) @ stdcall ImeSelect(long long) -@ stdcall ImeSetCompositionString(long long ptr long ptr long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) From 0778dfe2875f51f78ad8df1ac1a02f81cc85295b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 12 May 2023 07:23:00 +0200 Subject: [PATCH 301/758] win32u: Introduce a new ImeProcessKey call through NtUserMessageCall. (cherry picked from commit c7dc10b19256f27a1a6a6f2e32aadaeebef07eb4) --- dlls/imm32/ime.c | 17 +++++++++++++---- dlls/win32u/driver.c | 12 ++++++++++++ dlls/win32u/imm.c | 13 +++++++++++++ dlls/win32u/message.c | 3 +++ dlls/win32u/ntuser_private.h | 4 ++++ dlls/winex11.drv/ime.c | 7 ------- dlls/winex11.drv/winex11.drv.spec | 1 - dlls/wow64win/user.c | 13 +++++++++++++ include/ntuser.h | 14 ++++++++++++++ include/wine/gdi_driver.h | 1 + 10 files changed, 73 insertions(+), 12 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 3e5f34d6760..eee623a6eb5 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -439,11 +439,20 @@ BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag ) return TRUE; } -BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM key_data, BYTE *key_state ) +BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state ) { - FIXME( "himc %p, vkey %u, key_data %#Ix, key_state %p stub!\n", himc, vkey, key_data, key_state ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return FALSE; + struct ime_driver_call_params params = {.himc = himc, .state = state}; + INPUTCONTEXT *ctx; + LRESULT ret; + + TRACE( "himc %p, vkey %#x, lparam %#Ix, state %p\n", himc, vkey, lparam, state ); + + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + ret = NtUserMessageCall( ctx->hWnd, WINE_IME_PROCESS_KEY, vkey, lparam, ¶ms, + NtUserImeDriverCall, FALSE ); + ImmUnlockIMC( himc ); + + return ret; } UINT WINAPI ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index f0055b68d1e..e1dd1a1ebcf 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -716,6 +716,11 @@ static SHORT nulldrv_VkKeyScanEx( WCHAR ch, HKL layout ) return -256; /* use default implementation */ } +static UINT nulldrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BYTE *state ) +{ + return 0; +} + static void nulldrv_NotifyIMEStatus( HWND hwnd, UINT status ) { } @@ -1074,6 +1079,11 @@ static SHORT loaderdrv_VkKeyScanEx( WCHAR ch, HKL layout ) return load_driver()->pVkKeyScanEx( ch, layout ); } +static UINT loaderdrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BYTE *state ) +{ + return load_driver()->pImeProcessKey( himc, wparam, lparam, state ); +} + static void loaderdrv_NotifyIMEStatus( HWND hwnd, UINT status ) { return load_driver()->pNotifyIMEStatus( hwnd, status ); @@ -1185,6 +1195,7 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_ToUnicodeEx, loaderdrv_UnregisterHotKey, loaderdrv_VkKeyScanEx, + loaderdrv_ImeProcessKey, loaderdrv_NotifyIMEStatus, /* cursor/icon functions */ nulldrv_DestroyCursorIcon, @@ -1265,6 +1276,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(ToUnicodeEx); SET_USER_FUNC(UnregisterHotKey); SET_USER_FUNC(VkKeyScanEx); + SET_USER_FUNC(ImeProcessKey); SET_USER_FUNC(NotifyIMEStatus); SET_USER_FUNC(DestroyCursorIcon); SET_USER_FUNC(SetCursor); diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index 328a4a3eddf..b91c3617472 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -421,6 +421,19 @@ NTSTATUS WINAPI NtUserBuildHimcList( UINT thread_id, UINT count, HIMC *buffer, U return STATUS_SUCCESS; } +LRESULT ime_driver_call( HWND hwnd, enum wine_ime_call call, WPARAM wparam, LPARAM lparam, + struct ime_driver_call_params *params ) +{ + switch (call) + { + case WINE_IME_PROCESS_KEY: + return user_driver->pImeProcessKey( params->himc, wparam, lparam, params->state ); + default: + ERR( "Unknown IME driver call %#x\n", call ); + return 0; + } +} + /***************************************************************************** * NtUserNotifyIMEStatus (win32u.@) */ diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 62924119c7d..8c7548a396a 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3688,6 +3688,9 @@ LRESULT WINAPI NtUserMessageCall( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpa spy_exit_message( ansi, hwnd, msg, (LPARAM)result_info, wparam, lparam ); return 0; + case NtUserImeDriverCall: + return ime_driver_call( hwnd, msg, wparam, lparam, result_info ); + default: FIXME( "%p %x %lx %lx %p %x %x\n", hwnd, msg, (long)wparam, lparam, result_info, (int)type, ansi ); } diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 41a53837642..b7d2b633eb5 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -236,6 +236,10 @@ BOOL needs_ime_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void register_builtin_classes(void) DECLSPEC_HIDDEN; extern void register_desktop_class(void) DECLSPEC_HIDDEN; +/* imm.c */ +extern LRESULT ime_driver_call( HWND hwnd, enum wine_ime_call call, WPARAM wparam, LPARAM lparam, + struct ime_driver_call_params *params ) DECLSPEC_HIDDEN; + /* cursoricon.c */ HICON alloc_cursoricon_handle( BOOL is_icon ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index b46c2d86892..348be4f0ad6 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -477,13 +477,6 @@ static void IME_AddToSelected(HIMC hIMC) hSelectedFrom[hSelectedCount-1] = hIMC; } -BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) -{ - /* See the comment at the head of this file */ - TRACE("We do no processing via this route\n"); - return FALSE; -} - BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) { LPINPUTCONTEXT lpIMC; diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index 64154d709a2..8b286033250 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -13,4 +13,3 @@ #IME Interface @ stdcall ImeSelect(long long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) -@ stdcall ImeProcessKey(long long long ptr) diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c index f0b57f7c886..02c2d9d975e 100644 --- a/dlls/wow64win/user.c +++ b/dlls/wow64win/user.c @@ -3126,6 +3126,19 @@ NTSTATUS WINAPI wow64_NtUserMessageCall( UINT *args ) return message_call_32to64( hwnd, msg, wparam, lparam, LongToPtr( result32 ), type, ansi ); } + + case NtUserImeDriverCall: + { + struct + { + ULONG himc; + ULONG state; + } *params32 = result_info; + struct ime_driver_call_params params; + params.himc = UlongToPtr( params32->himc ); + params.state = UlongToPtr( params32->state ); + return NtUserMessageCall( hwnd, msg, wparam, lparam, ¶ms, type, ansi ); + } } return message_call_32to64( hwnd, msg, wparam, lparam, result_info, type, ansi ); diff --git a/include/ntuser.h b/include/ntuser.h index 198f7889020..86c18426701 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -305,6 +305,7 @@ enum NtUserSpyEnter = 0x0304, NtUserSpyExit = 0x0305, NtUserWinProcResult = 0x0306, + NtUserImeDriverCall = 0x0307, }; /* NtUserThunkedMenuItemInfo codes */ @@ -490,6 +491,19 @@ enum wine_internal_message #define IME_INTERNAL_HKL_ACTIVATE 0x19 #define IME_INTERNAL_HKL_DEACTIVATE 0x20 +/* builtin IME driver calls */ +enum wine_ime_call +{ + WINE_IME_PROCESS_KEY, +}; + +/* NtUserImeDriverCall params */ +struct ime_driver_call_params +{ + HIMC himc; + const BYTE *state; +}; + /* internal IME private */ typedef struct ime_private { diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 71227a2bf23..cc2dff5766d 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -287,6 +287,7 @@ struct user_driver_funcs void (*pUnregisterHotKey)(HWND, UINT, UINT); SHORT (*pVkKeyScanEx)(WCHAR, HKL); /* IME functions */ + UINT (*pImeProcessKey)(HIMC,UINT,UINT,const BYTE*); void (*pNotifyIMEStatus)(HWND,UINT); /* cursor/icon functions */ void (*pDestroyCursorIcon)(HCURSOR); From 38e6406f8a00bec6dd41f27ab672e3d7d57ae2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 2 May 2023 12:14:19 +0200 Subject: [PATCH 302/758] winemac: Use the ImeProcessKey driver entry to process IME input. (cherry picked from commit f185dc1701f2076758ca59233fffc93924da1814) --- dlls/winemac.drv/gdi.c | 1 + dlls/winemac.drv/ime.c | 90 +------------------------------ dlls/winemac.drv/keyboard.c | 29 ++++++---- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/macdrv_main.c | 30 ----------- dlls/winemac.drv/unixlib.h | 12 ----- dlls/winemac.drv/winemac.drv.spec | 1 - 7 files changed, 22 insertions(+), 143 deletions(-) diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index 9c594a5fff6..059b99a2465 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -302,6 +302,7 @@ static const struct user_driver_funcs macdrv_funcs = .pUpdateClipboard = macdrv_UpdateClipboard, .pUpdateLayeredWindow = macdrv_UpdateLayeredWindow, .pVkKeyScanEx = macdrv_VkKeyScanEx, + .pImeProcessKey = macdrv_ImeProcessKey, .pNotifyIMEStatus = macdrv_NotifyIMEStatus, .pWindowMessage = macdrv_WindowMessage, .pWindowPosChanged = macdrv_WindowPosChanged, diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 65a79df2570..8ce9f4cd1ef 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -122,18 +122,6 @@ static BOOL UnlockRealIMC(HIMC hIMC) return FALSE; } -static HIMCC ImeCreateBlankCompStr(void) -{ - HIMCC rc; - LPCOMPOSITIONSTRING ptr; - rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); - ptr = ImmLockIMCC(rc); - memset(ptr, 0, sizeof(COMPOSITIONSTRING)); - ptr->dwSize = sizeof(COMPOSITIONSTRING); - ImmUnlockIMCC(rc); - return rc; -} - static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset, LPBYTE target, LPBYTE source, DWORD* lenParam, DWORD* offsetParam, BOOL wchars) @@ -516,59 +504,6 @@ static void UpdateDataInDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd, BOOL sh ImmUnlockIMCC(lpIMC->hCompStr); } -BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) -{ - struct process_text_input_params params = - { - .himc = (UINT_PTR)hIMC, .vkey = LOWORD(vKey), .scan = HIWORD(lKeyData), - .repeat = !!(lKeyData >> 30), .key_state = lpbKeyState, - }; - LPINPUTCONTEXT lpIMC; - BOOL inIME; - UINT ret; - - TRACE("hIMC %p vKey 0x%04x lKeyData 0x%08Ix lpbKeyState %p\n", hIMC, vKey, lKeyData, lpbKeyState); - - switch (vKey) - { - case VK_SHIFT: - case VK_CONTROL: - case VK_CAPITAL: - case VK_MENU: - return FALSE; - } - - inIME = MACDRV_CALL(ime_using_input_method, NULL); - lpIMC = LockRealIMC(hIMC); - if (lpIMC) - { - LPIMEPRIVATE myPrivate; - HWND hwnd = input_context_get_ui_hwnd( lpIMC ); - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - - if (inIME && !myPrivate->bInternalState) - ImmSetOpenStatus(RealIMC(FROM_MACDRV), TRUE); - else if (!inIME && myPrivate->bInternalState) - { - ShowWindow( hwnd, SW_HIDE ); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = ImeCreateBlankCompStr(); - ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); - } - - myPrivate->bInternalState = inIME; - ImmUnlockIMCC(lpIMC->hPrivate); - } - UnlockRealIMC(hIMC); - - if (!inIME) return FALSE; - - TRACE( "Processing Mac 0x%04x\n", vKey ); - ret = MACDRV_CALL( ime_process_text_input, ¶ms ); - - return ret != 0; -} - BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) { LPINPUTCONTEXT lpIMC; @@ -613,39 +548,16 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC) { - UINT vkey; LPINPUTCONTEXT lpIMC; - LPIMEPRIVATE myPrivate; - HWND hwnd; TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC); - vkey = LOWORD(uVKey); - - if (vkey == VK_KANA || vkey == VK_KANJI || vkey == VK_MENU) - { - TRACE("Skipping metakey\n"); - return 0; - } - - lpIMC = LockRealIMC(hIMC); - hwnd = input_context_get_ui_hwnd( lpIMC ); - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (!myPrivate->bInternalState) - { - ImmUnlockIMCC(lpIMC->hPrivate); - UnlockRealIMC(hIMC); - return 0; - } - - ImmUnlockIMCC(lpIMC->hPrivate); - UnlockRealIMC(hIMC); - /* trigger the pending client_func_ime_set_text call */ MACDRV_CALL(ime_get_text_input, NULL); if ((lpIMC = LockRealIMC(hIMC))) { + HWND hwnd = input_context_get_ui_hwnd( lpIMC ); UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); UnlockRealIMC(hIMC); } diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 0f45873a831..14f0010e37e 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -1189,19 +1189,29 @@ void macdrv_hotkey_press(const macdrv_event *event) /*********************************************************************** - * macdrv_process_text_input + * ImeProcessKey (MACDRV.@) */ -NTSTATUS macdrv_ime_process_text_input(void *arg) +UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_state) { - struct process_text_input_params *params = arg; struct macdrv_thread_data *thread_data = macdrv_thread_data(); - void *himc = UlongToHandle(params->himc); - const BYTE *key_state = params->key_state; + WORD scan = HIWORD(lparam) & 0x1ff, vkey = LOWORD(wparam); + BOOL repeat = !!(lparam >> 30), pressed = !(lparam >> 31); unsigned int flags; int keyc, done = 0; - TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", params->vkey, params->scan, - params->repeat, himc); + TRACE("himc %p, scan %#x, vkey %#x, repeat %u, pressed %u\n", + himc, scan, vkey, repeat, pressed); + + if (!macdrv_using_input_method()) return 0; + + switch (vkey) + { + case VK_SHIFT: + case VK_CONTROL: + case VK_CAPITAL: + case VK_MENU: + return 0; + } flags = thread_data->last_modifiers; if (key_state[VK_SHIFT] & 0x80) @@ -1223,14 +1233,13 @@ NTSTATUS macdrv_ime_process_text_input(void *arg) /* Find the Mac keycode corresponding to the scan code */ for (keyc = 0; keyc < ARRAY_SIZE(thread_data->keyc2vkey); keyc++) - if (thread_data->keyc2vkey[keyc] == params->vkey) break; + if (thread_data->keyc2vkey[keyc] == vkey) break; if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey)) return 0; TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc); - macdrv_send_text_input_event(((params->scan & 0x8000) == 0), flags, params->repeat, keyc, - himc, &done); + macdrv_send_text_input_event(pressed, flags, repeat, keyc, himc, &done); while (!done) NtUserMsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0); return done > 0; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 71f1215da60..86b2e68e11b 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -162,6 +162,7 @@ extern BOOL macdrv_SetCursorPos(INT x, INT y) DECLSPEC_HIDDEN; extern BOOL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey) DECLSPEC_HIDDEN; extern void macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey) DECLSPEC_HIDDEN; extern SHORT macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl) DECLSPEC_HIDDEN; +extern UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *state) DECLSPEC_HIDDEN; extern UINT macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl) DECLSPEC_HIDDEN; extern INT macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, LPWSTR bufW, int bufW_size, UINT flags, HKL hkl) DECLSPEC_HIDDEN; @@ -277,7 +278,6 @@ extern NTSTATUS macdrv_dnd_get_formats(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_have_format(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_release(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_retain(void *arg) DECLSPEC_HIDDEN; -extern NTSTATUS macdrv_ime_process_text_input(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_ime_get_text_input(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_notify_icon(void *arg) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 9125574e35b..84369284444 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -605,12 +605,6 @@ NTSTATUS macdrv_client_func(enum macdrv_client_funcs id, const void *params, ULO } -static NTSTATUS macdrv_ime_using_input_method(void *arg) -{ - return macdrv_using_input_method(); -} - - static NTSTATUS macdrv_quit_result(void *arg) { struct quit_result_params *params = arg; @@ -626,9 +620,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = macdrv_dnd_have_format, macdrv_dnd_release, macdrv_dnd_retain, - macdrv_ime_process_text_input, macdrv_ime_get_text_input, - macdrv_ime_using_input_method, macdrv_init, macdrv_notify_icon, macdrv_quit_result, @@ -656,26 +648,6 @@ static NTSTATUS wow64_dnd_get_data(void *arg) return macdrv_dnd_get_data(¶ms); } -static NTSTATUS wow64_ime_process_text_input(void *arg) -{ - struct - { - UINT himc; - UINT vkey; - UINT scan; - UINT repeat; - ULONG key_state; - } *params32 = arg; - struct process_text_input_params params; - - params.himc = params32->himc; - params.vkey = params32->vkey; - params.scan = params32->scan; - params.repeat = params32->repeat; - params.key_state = UlongToPtr(params32->key_state); - return macdrv_ime_process_text_input(¶ms); -} - static NTSTATUS wow64_init(void *arg) { struct @@ -750,9 +722,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = macdrv_dnd_have_format, macdrv_dnd_release, macdrv_dnd_retain, - wow64_ime_process_text_input, macdrv_ime_get_text_input, - macdrv_ime_using_input_method, wow64_init, wow64_notify_icon, macdrv_quit_result, diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 1f7840a1546..219d0e3dd46 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -26,9 +26,7 @@ enum macdrv_funcs unix_dnd_have_format, unix_dnd_release, unix_dnd_retain, - unix_ime_process_text_input, unix_ime_get_text_input, - unix_ime_using_input_method, unix_init, unix_notify_icon, unix_quit_result, @@ -60,16 +58,6 @@ struct dnd_have_format_params UINT format; }; -/* macdrv_ime_process_text_input params */ -struct process_text_input_params -{ - UINT himc; - UINT vkey; - UINT scan; - UINT repeat; - const BYTE *key_state; -}; - /* macdrv_init params */ struct localized_string { diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index aca1c58cf13..27143ab560b 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -2,6 +2,5 @@ @ cdecl wine_notify_icon(long ptr) # IME -@ stdcall ImeProcessKey(long long long ptr) @ stdcall ImeSelect(long long) @ stdcall ImeToAsciiEx(long long ptr ptr long long) From 02f44db312b45295d25b409dc3575428e389fbfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 14:33:37 +0200 Subject: [PATCH 303/758] win32u: Introduce a new ImeToAsciiEx call through NtUserMessageCall. (cherry picked from commit bfa19f8c7e5a361fa72ca2c68ba47f350641158a) --- dlls/imm32/ime.c | 28 +++++++++++++++++++++++----- dlls/win32u/driver.c | 12 ++++++++++++ dlls/win32u/imm.c | 2 ++ dlls/winex11.drv/ime.c | 8 -------- dlls/winex11.drv/winex11.drv.spec | 1 - dlls/wow64win/user.c | 2 ++ include/ntuser.h | 3 +++ include/wine/gdi_driver.h | 2 ++ 8 files changed, 44 insertions(+), 14 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index eee623a6eb5..db9cdfa9a3b 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -455,12 +455,30 @@ BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state ) return ret; } -UINT WINAPI ImeToAsciiEx( UINT vkey, UINT scan_code, BYTE *key_state, TRANSMSGLIST *msgs, UINT state, HIMC himc ) +UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc ) { - FIXME( "vkey %u, scan_code %u, key_state %p, msgs %p, state %u, himc %p stub!\n", - vkey, scan_code, key_state, msgs, state, himc ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return 0; + struct ime_driver_call_params params = {.himc = himc, .state = state}; + COMPOSITIONSTRING *compstr; + UINT count = 0; + INPUTCONTEXT *ctx; + NTSTATUS status; + + TRACE( "vkey %#x, vsc %#x, state %p, msgs %p, flags %#x, himc %p\n", + vkey, vsc, state, msgs, flags, himc ); + + if (!(ctx = ImmLockIMC( himc ))) return 0; + if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) goto done; + + params.compstr = compstr; + status = NtUserMessageCall( ctx->hWnd, WINE_IME_TO_ASCII_EX, vkey, vsc, ¶ms, + NtUserImeDriverCall, FALSE ); + if (status) WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status ); + + ImmUnlockIMCC( ctx->hCompStr ); + +done: + ImmUnlockIMC( himc ); + return count; } BOOL WINAPI ImeConfigure( HKL hkl, HWND hwnd, DWORD mode, void *data ) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index e1dd1a1ebcf..5b8cbb56813 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -721,6 +721,11 @@ static UINT nulldrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BY return 0; } +static UINT nulldrv_ImeToAsciiEx( UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc ) +{ + return 0; +} + static void nulldrv_NotifyIMEStatus( HWND hwnd, UINT status ) { } @@ -1084,6 +1089,11 @@ static UINT loaderdrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const return load_driver()->pImeProcessKey( himc, wparam, lparam, state ); } +static UINT loaderdrv_ImeToAsciiEx( UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc ) +{ + return load_driver()->pImeToAsciiEx( vkey, vsc, state, compstr, himc ); +} + static void loaderdrv_NotifyIMEStatus( HWND hwnd, UINT status ) { return load_driver()->pNotifyIMEStatus( hwnd, status ); @@ -1196,6 +1206,7 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_UnregisterHotKey, loaderdrv_VkKeyScanEx, loaderdrv_ImeProcessKey, + loaderdrv_ImeToAsciiEx, loaderdrv_NotifyIMEStatus, /* cursor/icon functions */ nulldrv_DestroyCursorIcon, @@ -1277,6 +1288,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(UnregisterHotKey); SET_USER_FUNC(VkKeyScanEx); SET_USER_FUNC(ImeProcessKey); + SET_USER_FUNC(ImeToAsciiEx); SET_USER_FUNC(NotifyIMEStatus); SET_USER_FUNC(DestroyCursorIcon); SET_USER_FUNC(SetCursor); diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index b91c3617472..287596e60ef 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -428,6 +428,8 @@ LRESULT ime_driver_call( HWND hwnd, enum wine_ime_call call, WPARAM wparam, LPAR { case WINE_IME_PROCESS_KEY: return user_driver->pImeProcessKey( params->himc, wparam, lparam, params->state ); + case WINE_IME_TO_ASCII_EX: + return user_driver->pImeToAsciiEx( wparam, lparam, params->state, params->compstr, params->himc ); default: ERR( "Unknown IME driver call %#x\n", call ); return 0; diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 348be4f0ad6..b375b7f8e2a 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -514,14 +514,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } -UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, - TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC) -{ - /* See the comment at the head of this file */ - TRACE("We do no processing via this route\n"); - return 0; -} - /* Interfaces to XIM and other parts of winex11drv */ NTSTATUS x11drv_ime_set_open_status( UINT open ) diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index 8b286033250..c8d52d07f6d 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -12,4 +12,3 @@ #IME Interface @ stdcall ImeSelect(long long) -@ stdcall ImeToAsciiEx(long long ptr ptr long long) diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c index 02c2d9d975e..11ef806929a 100644 --- a/dlls/wow64win/user.c +++ b/dlls/wow64win/user.c @@ -3133,10 +3133,12 @@ NTSTATUS WINAPI wow64_NtUserMessageCall( UINT *args ) { ULONG himc; ULONG state; + ULONG compstr; } *params32 = result_info; struct ime_driver_call_params params; params.himc = UlongToPtr( params32->himc ); params.state = UlongToPtr( params32->state ); + params.compstr = UlongToPtr( params32->compstr ); return NtUserMessageCall( hwnd, msg, wparam, lparam, ¶ms, type, ansi ); } } diff --git a/include/ntuser.h b/include/ntuser.h index 86c18426701..45082f67dfb 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -22,6 +22,7 @@ #include #include #include +#include #include /* KernelCallbackTable codes, not compatible with Windows */ @@ -495,6 +496,7 @@ enum wine_internal_message enum wine_ime_call { WINE_IME_PROCESS_KEY, + WINE_IME_TO_ASCII_EX, }; /* NtUserImeDriverCall params */ @@ -502,6 +504,7 @@ struct ime_driver_call_params { HIMC himc; const BYTE *state; + COMPOSITIONSTRING *compstr; }; /* internal IME private */ diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index cc2dff5766d..c73b29015fc 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -23,6 +23,7 @@ #include "winternl.h" #include "ntuser.h" +#include "immdev.h" #include "ddk/d3dkmthk.h" #include "wine/list.h" @@ -288,6 +289,7 @@ struct user_driver_funcs SHORT (*pVkKeyScanEx)(WCHAR, HKL); /* IME functions */ UINT (*pImeProcessKey)(HIMC,UINT,UINT,const BYTE*); + UINT (*pImeToAsciiEx)(UINT,UINT,const BYTE*,COMPOSITIONSTRING*,HIMC); void (*pNotifyIMEStatus)(HWND,UINT); /* cursor/icon functions */ void (*pDestroyCursorIcon)(HCURSOR); From 0e59c7feaad175936cbe9e201f5e6172853e6976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 11 May 2023 08:39:30 +0200 Subject: [PATCH 304/758] winemac: Use the ImeToAsciiEx driver entry to retrieve IME result. (cherry picked from commit 54bbdb83e9a99615815e028e7423e5f125dc7754) --- dlls/winemac.drv/event.c | 6 +++-- dlls/winemac.drv/gdi.c | 1 + dlls/winemac.drv/ime.c | 39 ------------------------------- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/macdrv_main.c | 2 -- dlls/winemac.drv/unixlib.h | 1 - dlls/winemac.drv/winemac.drv.spec | 1 - 7 files changed, 6 insertions(+), 46 deletions(-) diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 6a695f87e58..cc2dc352c32 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -205,10 +205,12 @@ static void macdrv_sent_text_input(const macdrv_event *event) /*********************************************************************** - * macdrv_ime_get_text_input + * ImeToAsciiEx (MACDRV.@) */ -NTSTATUS macdrv_ime_get_text_input(void *arg) +UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc) { + TRACE_(imm)("vkey %#x, vsc %#x, state %p, compstr %p, himc %p\n", vkey, vsc, state, compstr, himc); + if (ime_update.result_params) { macdrv_client_func(client_func_ime_set_text, ime_update.result_params, ime_update.result_size); diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index 059b99a2465..ef19b39cade 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -303,6 +303,7 @@ static const struct user_driver_funcs macdrv_funcs = .pUpdateLayeredWindow = macdrv_UpdateLayeredWindow, .pVkKeyScanEx = macdrv_VkKeyScanEx, .pImeProcessKey = macdrv_ImeProcessKey, + .pImeToAsciiEx = macdrv_ImeToAsciiEx, .pNotifyIMEStatus = macdrv_NotifyIMEStatus, .pWindowMessage = macdrv_WindowMessage, .pWindowPosChanged = macdrv_WindowPosChanged, diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 8ce9f4cd1ef..aab93ecc629 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -484,26 +484,6 @@ static void IME_AddToSelected(HIMC hIMC) hSelectedFrom[hSelectedCount - 1] = hIMC; } -static void UpdateDataInDefaultIMEWindow(INPUTCONTEXT *lpIMC, HWND hwnd, BOOL showable) -{ - LPCOMPOSITIONSTRING compstr; - - if (lpIMC->hCompStr) - compstr = ImmLockIMCC(lpIMC->hCompStr); - else - compstr = NULL; - - if (compstr == NULL || compstr->dwCompStrLen == 0) - ShowWindow(hwnd, SW_HIDE); - else if (showable) - ShowWindow(hwnd, SW_SHOWNOACTIVATE); - - RedrawWindow(hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE); - - if (compstr != NULL) - ImmUnlockIMCC(lpIMC->hCompStr); -} - BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) { LPINPUTCONTEXT lpIMC; @@ -545,25 +525,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } -UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, const LPBYTE lpbKeyState, - TRANSMSGLIST *lpdwTransKey, UINT fuState, HIMC hIMC) -{ - LPINPUTCONTEXT lpIMC; - - TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC); - - /* trigger the pending client_func_ime_set_text call */ - MACDRV_CALL(ime_get_text_input, NULL); - - if ((lpIMC = LockRealIMC(hIMC))) - { - HWND hwnd = input_context_get_ui_hwnd( lpIMC ); - UpdateDataInDefaultIMEWindow( lpIMC, hwnd, FALSE ); - UnlockRealIMC(hIMC); - } - return 0; -} - static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid) { LPINPUTCONTEXT lpIMC; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 86b2e68e11b..1a2bb684583 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -163,6 +163,7 @@ extern BOOL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey) DECLSPEC extern void macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey) DECLSPEC_HIDDEN; extern SHORT macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl) DECLSPEC_HIDDEN; extern UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *state) DECLSPEC_HIDDEN; +extern UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc) DECLSPEC_HIDDEN; extern UINT macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl) DECLSPEC_HIDDEN; extern INT macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, LPWSTR bufW, int bufW_size, UINT flags, HKL hkl) DECLSPEC_HIDDEN; @@ -278,7 +279,6 @@ extern NTSTATUS macdrv_dnd_get_formats(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_have_format(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_release(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_dnd_retain(void *arg) DECLSPEC_HIDDEN; -extern NTSTATUS macdrv_ime_get_text_input(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_notify_icon(void *arg) DECLSPEC_HIDDEN; extern NTSTATUS macdrv_client_func(enum macdrv_client_funcs func, const void *params, diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 84369284444..cafd2f13347 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -620,7 +620,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = macdrv_dnd_have_format, macdrv_dnd_release, macdrv_dnd_retain, - macdrv_ime_get_text_input, macdrv_init, macdrv_notify_icon, macdrv_quit_result, @@ -722,7 +721,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = macdrv_dnd_have_format, macdrv_dnd_release, macdrv_dnd_retain, - macdrv_ime_get_text_input, wow64_init, wow64_notify_icon, macdrv_quit_result, diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 219d0e3dd46..08c36860dd7 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -26,7 +26,6 @@ enum macdrv_funcs unix_dnd_have_format, unix_dnd_release, unix_dnd_retain, - unix_ime_get_text_input, unix_init, unix_notify_icon, unix_quit_result, diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 27143ab560b..68bebc794aa 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -3,4 +3,3 @@ # IME @ stdcall ImeSelect(long long) -@ stdcall ImeToAsciiEx(long long ptr ptr long long) From 287a44a0354d5a8fdcdbfe2c4936b591deab77b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 12 May 2023 08:54:55 +0200 Subject: [PATCH 305/758] winemac: Compute the required COMPOSITIONSTRING size in ImeToAsciiEx. (cherry picked from commit 3cee423da03383ac7a94e6bf422c676c565300b4) --- dlls/winemac.drv/event.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index cc2dc352c32..d7e76a5cbc7 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -26,6 +26,8 @@ #include "config.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "macdrv.h" #include "oleidl.h" @@ -34,6 +36,8 @@ WINE_DECLARE_DEBUG_CHANNEL(imm); struct ime_update { + WCHAR *comp_str; + WCHAR *result_str; struct ime_set_text_params *comp_params; DWORD comp_size; struct ime_set_text_params *result_params; @@ -168,7 +172,7 @@ static void macdrv_im_set_text(const macdrv_event *event) if (event->im_set_text.text) length = CFStringGetLength(event->im_set_text.text); - size = offsetof(struct ime_set_text_params, text[length]); + size = offsetof(struct ime_set_text_params, text[length + 1]); if (!(params = malloc(size))) return; params->hwnd = HandleToUlong(hwnd); params->himc = (UINT_PTR)event->im_set_text.himc; @@ -177,20 +181,24 @@ static void macdrv_im_set_text(const macdrv_event *event) if (length) CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), params->text); + params->text[length] = 0; free(ime_update.comp_params); ime_update.comp_params = NULL; + ime_update.comp_str = NULL; if (params->complete) { free(ime_update.result_params); ime_update.result_params = params; ime_update.result_size = size; + ime_update.result_str = params->text; } else { ime_update.comp_params = params; ime_update.comp_size = size; + ime_update.comp_str = params->text; } } @@ -200,7 +208,7 @@ static void macdrv_im_set_text(const macdrv_event *event) static void macdrv_sent_text_input(const macdrv_event *event) { TRACE_(imm)("handled: %s\n", event->sent_text_input.handled ? "TRUE" : "FALSE"); - *event->sent_text_input.done = event->sent_text_input.handled || ime_update.result_params ? 1 : -1; + *event->sent_text_input.done = event->sent_text_input.handled || ime_update.result_str ? 1 : -1; } @@ -209,19 +217,47 @@ static void macdrv_sent_text_input(const macdrv_event *event) */ UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc) { + UINT needed = sizeof(COMPOSITIONSTRING), comp_len, result_len; + struct ime_update *update = &ime_update; + TRACE_(imm)("vkey %#x, vsc %#x, state %p, compstr %p, himc %p\n", vkey, vsc, state, compstr, himc); + if (!update->comp_str) comp_len = 0; + else + { + comp_len = wcslen(update->comp_str); + needed += comp_len * sizeof(WCHAR); /* GCS_COMPSTR */ + needed += comp_len; /* GCS_COMPATTR */ + needed += 2 * sizeof(DWORD); /* GCS_COMPCLAUSE */ + } + + if (!update->result_str) result_len = 0; + else + { + result_len = wcslen(update->result_str); + needed += result_len * sizeof(WCHAR); /* GCS_RESULTSTR */ + needed += 2 * sizeof(DWORD); /* GCS_RESULTCLAUSE */ + } + if (ime_update.result_params) { macdrv_client_func(client_func_ime_set_text, ime_update.result_params, ime_update.result_size); free(ime_update.result_params); ime_update.result_params = NULL; + ime_update.result_str = NULL; } if (ime_update.comp_params) { macdrv_client_func(client_func_ime_set_text, ime_update.comp_params, ime_update.comp_size); free(ime_update.comp_params); ime_update.comp_params = NULL; + ime_update.comp_str = NULL; + } + + if (compstr->dwSize < needed) + { + compstr->dwSize = needed; + return STATUS_BUFFER_TOO_SMALL; } return 0; From b76f825d086ab3d144ae9d48c1e73628be46c8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 11 May 2023 08:22:08 +0200 Subject: [PATCH 306/758] imm32: Resize the composition string if the driver requested so. (cherry picked from commit 4870a9dfad3527c9eca7ed0ac9481281cbdfc254) --- dlls/imm32/ime.c | 22 +++++++++++++++++----- dlls/imm32/imm_private.h | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index db9cdfa9a3b..b544035dbd7 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -457,9 +457,8 @@ BOOL WINAPI ImeProcessKey( HIMC himc, UINT vkey, LPARAM lparam, BYTE *state ) UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc ) { - struct ime_driver_call_params params = {.himc = himc, .state = state}; COMPOSITIONSTRING *compstr; - UINT count = 0; + UINT size, count = 0; INPUTCONTEXT *ctx; NTSTATUS status; @@ -468,10 +467,23 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, if (!(ctx = ImmLockIMC( himc ))) return 0; if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) goto done; + size = compstr->dwSize; + + do + { + struct ime_driver_call_params params = {.himc = himc, .state = state}; + HIMCC himcc; + + ImmUnlockIMCC( ctx->hCompStr ); + if (!(himcc = ImmReSizeIMCC( ctx->hCompStr, size ))) goto done; + if (!(compstr = ImmLockIMCC( (ctx->hCompStr = himcc) ))) goto done; + + params.compstr = compstr; + status = NtUserMessageCall( ctx->hWnd, WINE_IME_TO_ASCII_EX, vkey, vsc, ¶ms, + NtUserImeDriverCall, FALSE ); + size = compstr->dwSize; + } while (status == STATUS_BUFFER_TOO_SMALL); - params.compstr = compstr; - status = NtUserMessageCall( ctx->hWnd, WINE_IME_TO_ASCII_EX, vkey, vsc, ¶ms, - NtUserImeDriverCall, FALSE ); if (status) WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status ); ImmUnlockIMCC( ctx->hCompStr ); diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h index 8a957494ce9..4cc4acda6c1 100644 --- a/dlls/imm32/imm_private.h +++ b/dlls/imm32/imm_private.h @@ -19,6 +19,8 @@ #include #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "wingdi.h" From 0ce71246c9cd78a38c7a79949e435fa26c007ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 11 May 2023 08:47:06 +0200 Subject: [PATCH 307/758] winemac: Write the IME strings to the COMPOSITIONSTRING in ImeToAsciiEx. (cherry picked from commit 8c08ffcdec5d5b9a098a14ceb300cffd2c3a3980) --- dlls/winemac.drv/event.c | 58 +++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index d7e76a5cbc7..a5a0fac8ad4 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -36,6 +36,7 @@ WINE_DECLARE_DEBUG_CHANNEL(imm); struct ime_update { + DWORD cursor_pos; WCHAR *comp_str; WCHAR *result_str; struct ime_set_text_params *comp_params; @@ -199,6 +200,7 @@ static void macdrv_im_set_text(const macdrv_event *event) ime_update.comp_params = params; ime_update.comp_size = size; ime_update.comp_str = params->text; + ime_update.cursor_pos = event->im_set_text.cursor_pos; } } @@ -219,6 +221,7 @@ UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRI { UINT needed = sizeof(COMPOSITIONSTRING), comp_len, result_len; struct ime_update *update = &ime_update; + void *dst; TRACE_(imm)("vkey %#x, vsc %#x, state %p, compstr %p, himc %p\n", vkey, vsc, state, compstr, himc); @@ -239,6 +242,55 @@ UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRI needed += 2 * sizeof(DWORD); /* GCS_RESULTCLAUSE */ } + if (compstr->dwSize < needed) + { + compstr->dwSize = needed; + return STATUS_BUFFER_TOO_SMALL; + } + + memset( compstr, 0, sizeof(*compstr) ); + compstr->dwSize = sizeof(*compstr); + + if (update->comp_str) + { + compstr->dwCursorPos = update->cursor_pos; + + compstr->dwCompStrLen = comp_len; + compstr->dwCompStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompStrOffset; + memcpy(dst, update->comp_str, compstr->dwCompStrLen * sizeof(WCHAR)); + compstr->dwSize += compstr->dwCompStrLen * sizeof(WCHAR); + + compstr->dwCompClauseLen = 2 * sizeof(DWORD); + compstr->dwCompClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwCompStrLen; + compstr->dwSize += compstr->dwCompClauseLen; + + compstr->dwCompAttrLen = compstr->dwCompStrLen; + compstr->dwCompAttrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompAttrOffset; + memset(dst, ATTR_INPUT, compstr->dwCompAttrLen); + compstr->dwSize += compstr->dwCompAttrLen; + } + + if (update->result_str) + { + compstr->dwResultStrLen = result_len; + compstr->dwResultStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwResultStrOffset; + memcpy(dst, update->result_str, compstr->dwResultStrLen * sizeof(WCHAR)); + compstr->dwSize += compstr->dwResultStrLen * sizeof(WCHAR); + + compstr->dwResultClauseLen = 2 * sizeof(DWORD); + compstr->dwResultClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwResultClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwResultStrLen; + compstr->dwSize += compstr->dwResultClauseLen; + } + if (ime_update.result_params) { macdrv_client_func(client_func_ime_set_text, ime_update.result_params, ime_update.result_size); @@ -254,12 +306,6 @@ UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRI ime_update.comp_str = NULL; } - if (compstr->dwSize < needed) - { - compstr->dwSize = needed; - return STATUS_BUFFER_TOO_SMALL; - } - return 0; } From 6f6251449f858952522e47055187c9b54612477a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 15:50:10 +0200 Subject: [PATCH 308/758] winemac: Generate IME messages from the default ImeToAsciiEx implementation. (cherry picked from commit 8ae0c308233e61cfc01ef52886077960d6513d93) --- dlls/imm32/ime.c | 38 ++- dlls/winemac.drv/dllmain.c | 1 - dlls/winemac.drv/event.c | 69 +++--- dlls/winemac.drv/ime.c | 433 ---------------------------------- dlls/winemac.drv/macdrv.h | 3 + dlls/winemac.drv/macdrv_dll.h | 1 - dlls/winemac.drv/unixlib.h | 11 - 7 files changed, 62 insertions(+), 494 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index b544035dbd7..c615e3406c9 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -164,13 +164,13 @@ static void ime_send_message( HIMC himc, UINT message, WPARAM wparam, LPARAM lpa ImmGenerateMessage( himc ); } -static void ime_set_composition_status( HIMC himc, BOOL composition ) +static UINT ime_set_composition_status( HIMC himc, BOOL composition ) { struct ime_private *priv; INPUTCONTEXT *ctx; UINT msg = 0; - if (!(ctx = ImmLockIMC( himc ))) return; + if (!(ctx = ImmLockIMC( himc ))) return 0; if ((priv = ImmLockIMCC( ctx->hPrivate ))) { if (!priv->bInComposition && composition) msg = WM_IME_STARTCOMPOSITION; @@ -180,7 +180,7 @@ static void ime_set_composition_status( HIMC himc, BOOL composition ) } ImmUnlockIMC( himc ); - if (msg) ime_send_message( himc, msg, 0, 0 ); + return msg; } static void ime_ui_paint( HIMC himc, HWND hwnd ) @@ -485,10 +485,35 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, } while (status == STATUS_BUFFER_TOO_SMALL); if (status) WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status ); + else + { + TRANSMSG status_msg = {.message = ime_set_composition_status( himc, !!compstr->dwCompStrOffset )}; + if (status_msg.message) msgs->TransMsg[count++] = status_msg; + + if (compstr->dwResultStrLen) + { + const WCHAR *result = (WCHAR *)((BYTE *)compstr + compstr->dwResultStrOffset); + TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = result[0], .lParam = GCS_RESULTSTR}; + if (compstr->dwResultClauseOffset) msg.lParam |= GCS_RESULTCLAUSE; + msgs->TransMsg[count++] = msg; + } + + if (compstr->dwCompStrLen) + { + const WCHAR *comp = (WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset); + TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = comp[0], .lParam = GCS_COMPSTR | GCS_CURSORPOS | GCS_DELTASTART}; + if (compstr->dwCompAttrOffset) msg.lParam |= GCS_COMPATTR; + if (compstr->dwCompClauseOffset) msg.lParam |= GCS_COMPCLAUSE; + else msg.lParam |= CS_INSERTCHAR|CS_NOMOVECARET; + msgs->TransMsg[count++] = msg; + } + } ImmUnlockIMCC( ctx->hCompStr ); done: + if (count >= msgs->uMsgCount) FIXME( "More than %u messages queued, messages possibly lost\n", msgs->uMsgCount ); + else TRACE( "Returning %u messages queued\n", count ); ImmUnlockIMC( himc ); return count; } @@ -524,10 +549,10 @@ BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, D FIXME( "index %#lx not implemented\n", index ); else { - UINT flags = GCS_COMPSTR | GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART | GCS_CURSORPOS; + UINT msg, flags = GCS_COMPSTR | GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART | GCS_CURSORPOS; WCHAR wparam = comp && comp_len >= sizeof(WCHAR) ? *(WCHAR *)comp : 0; input_context_set_comp_str( ctx, comp, comp_len / sizeof(WCHAR) ); - ime_set_composition_status( himc, TRUE ); + if ((msg = ime_set_composition_status( himc, TRUE ))) ime_send_message( himc, msg, 0, 0 ); ime_send_message( himc, WM_IME_COMPOSITION, wparam, flags ); } @@ -540,6 +565,7 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) { struct ime_private *priv; INPUTCONTEXT *ctx; + UINT msg; TRACE( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value ); @@ -562,7 +588,7 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) if (!ctx->fOpen) { input_context_set_comp_str( ctx, NULL, 0 ); - ime_set_composition_status( himc, FALSE ); + if ((msg = ime_set_composition_status( himc, TRUE ))) ime_send_message( himc, msg, 0, 0 ); } NtUserNotifyIMEStatus( ctx->hWnd, ctx->fOpen ); break; diff --git a/dlls/winemac.drv/dllmain.c b/dlls/winemac.drv/dllmain.c index 41e9e908b61..265d6e46d5c 100644 --- a/dlls/winemac.drv/dllmain.c +++ b/dlls/winemac.drv/dllmain.c @@ -375,7 +375,6 @@ static const kernel_callback kernel_callbacks[] = macdrv_dnd_query_drop, macdrv_dnd_query_exited, macdrv_ime_query_char_rect, - macdrv_ime_set_text, }; C_ASSERT(NtUserDriverCallbackFirst + ARRAYSIZE(kernel_callbacks) == client_func_last); diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index a5a0fac8ad4..1565cd9cbdd 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -34,15 +34,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(event); WINE_DECLARE_DEBUG_CHANNEL(imm); +/* IME works synchronously, key input is passed from ImeProcessKey, to the + * host IME. We wait for it to be handled, or not, which is notified using + * the sent_text_input event. Meanwhile, while processing the key, the host + * IME may send one or more im_set_text events to update the input text. + * + * If ImeProcessKey returns TRUE, ImeToAsciiEx is then be called to retrieve + * the composition string updates. We use ime_update.comp_str != NULL as flag that + * composition is started, even if the preedit text is empty. + * + * If ImeProcessKey returns FALSE, ImeToAsciiEx will not be called. + */ struct ime_update { DWORD cursor_pos; WCHAR *comp_str; WCHAR *result_str; - struct ime_set_text_params *comp_params; - DWORD comp_size; - struct ime_set_text_params *result_params; - DWORD result_size; }; static struct ime_update ime_update; @@ -164,42 +171,33 @@ static macdrv_event_mask get_event_mask(DWORD mask) static void macdrv_im_set_text(const macdrv_event *event) { HWND hwnd = macdrv_get_window_hwnd(event->window); - struct ime_set_text_params *params; - CFIndex length = 0, size; + CFIndex length = 0; + WCHAR *text = NULL; TRACE_(imm)("win %p/%p himc %p text %s complete %u\n", hwnd, event->window, event->im_set_text.himc, debugstr_cf(event->im_set_text.text), event->im_set_text.complete); if (event->im_set_text.text) + { length = CFStringGetLength(event->im_set_text.text); + if (!(text = malloc((length + 1) * sizeof(WCHAR)))) return; + if (length) CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), text); + text[length] = 0; + } - size = offsetof(struct ime_set_text_params, text[length + 1]); - if (!(params = malloc(size))) return; - params->hwnd = HandleToUlong(hwnd); - params->himc = (UINT_PTR)event->im_set_text.himc; - params->cursor_pos = event->im_set_text.cursor_pos; - params->complete = event->im_set_text.complete; - - if (length) - CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), params->text); - params->text[length] = 0; - - free(ime_update.comp_params); - ime_update.comp_params = NULL; + /* discard any pending comp text */ + free(ime_update.comp_str); ime_update.comp_str = NULL; + ime_update.cursor_pos = -1; - if (params->complete) + if (event->im_set_text.complete) { - free(ime_update.result_params); - ime_update.result_params = params; - ime_update.result_size = size; - ime_update.result_str = params->text; + free(ime_update.result_str); + ime_update.result_str = text; } else { - ime_update.comp_params = params; - ime_update.comp_size = size; - ime_update.comp_str = params->text; + ime_update.comp_str = text; ime_update.cursor_pos = event->im_set_text.cursor_pos; } } @@ -291,21 +289,8 @@ UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, const BYTE *state, COMPOSITIONSTRI compstr->dwSize += compstr->dwResultClauseLen; } - if (ime_update.result_params) - { - macdrv_client_func(client_func_ime_set_text, ime_update.result_params, ime_update.result_size); - free(ime_update.result_params); - ime_update.result_params = NULL; - ime_update.result_str = NULL; - } - if (ime_update.comp_params) - { - macdrv_client_func(client_func_ime_set_text, ime_update.comp_params, ime_update.comp_size); - free(ime_update.comp_params); - ime_update.comp_params = NULL; - ime_update.comp_str = NULL; - } - + free(update->result_str); + update->result_str = NULL; return 0; } diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index aab93ecc629..87adab88876 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -122,313 +122,6 @@ static BOOL UnlockRealIMC(HIMC hIMC) return FALSE; } -static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset, - LPBYTE target, LPBYTE source, DWORD* lenParam, - DWORD* offsetParam, BOOL wchars) -{ - if (origLen > 0 && origOffset > 0) - { - int truelen = origLen; - if (wchars) - truelen *= sizeof(WCHAR); - - memcpy(&target[currentOffset], &source[origOffset], truelen); - - *lenParam = origLen; - *offsetParam = currentOffset; - currentOffset += truelen; - } - return currentOffset; -} - -static HIMCC updateCompStr(HIMCC old, LPCWSTR compstr, DWORD len, DWORD *flags) -{ - /* We need to make sure the CompStr, CompClause and CompAttr fields are all - * set and correct. */ - int needed_size; - HIMCC rc; - LPBYTE newdata = NULL; - LPBYTE olddata = NULL; - LPCOMPOSITIONSTRING new_one; - LPCOMPOSITIONSTRING lpcs = NULL; - INT current_offset = 0; - - TRACE("%s, %li\n", debugstr_wn(compstr, len), len); - - if (old == NULL && compstr == NULL && len == 0) - return NULL; - - if (compstr == NULL && len != 0) - { - ERR("compstr is NULL however we have a len! Please report\n"); - len = 0; - } - - if (old != NULL) - { - olddata = ImmLockIMCC(old); - lpcs = (LPCOMPOSITIONSTRING)olddata; - } - - needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + - len + sizeof(DWORD) * 2; - - if (lpcs != NULL) - { - needed_size += lpcs->dwCompReadAttrLen; - needed_size += lpcs->dwCompReadClauseLen; - needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwResultReadClauseLen; - needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwResultClauseLen; - needed_size += lpcs->dwResultStrLen * sizeof(WCHAR); - needed_size += lpcs->dwPrivateSize; - } - rc = ImmCreateIMCC(needed_size); - newdata = ImmLockIMCC(rc); - new_one = (LPCOMPOSITIONSTRING)newdata; - - new_one->dwSize = needed_size; - current_offset = sizeof(COMPOSITIONSTRING); - if (lpcs != NULL) - { - current_offset = updateField(lpcs->dwCompReadAttrLen, - lpcs->dwCompReadAttrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadAttrLen, - &new_one->dwCompReadAttrOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadClauseLen, - lpcs->dwCompReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadClauseLen, - &new_one->dwCompReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadStrLen, - lpcs->dwCompReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadStrLen, - &new_one->dwCompReadStrOffset, TRUE); - - /* new CompAttr, CompClause, CompStr, dwCursorPos */ - new_one->dwDeltaStart = 0; - new_one->dwCursorPos = lpcs->dwCursorPos; - - current_offset = updateField(lpcs->dwResultReadClauseLen, - lpcs->dwResultReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadClauseLen, - &new_one->dwResultReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwResultReadStrLen, - lpcs->dwResultReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadStrLen, - &new_one->dwResultReadStrOffset, TRUE); - - current_offset = updateField(lpcs->dwResultClauseLen, - lpcs->dwResultClauseOffset, - current_offset, newdata, olddata, - &new_one->dwResultClauseLen, - &new_one->dwResultClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwResultStrLen, - lpcs->dwResultStrOffset, - current_offset, newdata, olddata, - &new_one->dwResultStrLen, - &new_one->dwResultStrOffset, TRUE); - - current_offset = updateField(lpcs->dwPrivateSize, - lpcs->dwPrivateOffset, - current_offset, newdata, olddata, - &new_one->dwPrivateSize, - &new_one->dwPrivateOffset, FALSE); - } - else - { - new_one->dwCursorPos = len; - *flags |= GCS_CURSORPOS; - } - - /* set new data */ - /* CompAttr */ - new_one->dwCompAttrLen = len; - if (len > 0) - { - new_one->dwCompAttrOffset = current_offset; - memset(&newdata[current_offset], ATTR_INPUT, len); - current_offset += len; - } - - /* CompClause */ - if (len > 0) - { - new_one->dwCompClauseLen = sizeof(DWORD) * 2; - new_one->dwCompClauseOffset = current_offset; - *(DWORD*)&newdata[current_offset] = 0; - current_offset += sizeof(DWORD); - *(DWORD*)&newdata[current_offset] = len; - current_offset += sizeof(DWORD); - } - else - new_one->dwCompClauseLen = 0; - - /* CompStr */ - new_one->dwCompStrLen = len; - if (len > 0) - { - new_one->dwCompStrOffset = current_offset; - memcpy(&newdata[current_offset], compstr, len * sizeof(WCHAR)); - } - - - ImmUnlockIMCC(rc); - if (lpcs) - ImmUnlockIMCC(old); - - return rc; -} - -static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len) -{ - /* we need to make sure the ResultStr and ResultClause fields are all - * set and correct */ - int needed_size; - HIMCC rc; - LPBYTE newdata = NULL; - LPBYTE olddata = NULL; - LPCOMPOSITIONSTRING new_one; - LPCOMPOSITIONSTRING lpcs = NULL; - INT current_offset = 0; - - TRACE("%s, %li\n", debugstr_wn(resultstr, len), len); - - if (old == NULL && resultstr == NULL && len == 0) - return NULL; - - if (resultstr == NULL && len != 0) - { - ERR("resultstr is NULL however we have a len! Please report\n"); - len = 0; - } - - if (old != NULL) - { - olddata = ImmLockIMCC(old); - lpcs = (LPCOMPOSITIONSTRING)olddata; - } - - needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + - sizeof(DWORD) * 2; - - if (lpcs != NULL) - { - needed_size += lpcs->dwCompReadAttrLen; - needed_size += lpcs->dwCompReadClauseLen; - needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwCompAttrLen; - needed_size += lpcs->dwCompClauseLen; - needed_size += lpcs->dwCompStrLen * sizeof(WCHAR); - needed_size += lpcs->dwResultReadClauseLen; - needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwPrivateSize; - } - rc = ImmCreateIMCC(needed_size); - newdata = ImmLockIMCC(rc); - new_one = (LPCOMPOSITIONSTRING)newdata; - - new_one->dwSize = needed_size; - current_offset = sizeof(COMPOSITIONSTRING); - if (lpcs != NULL) - { - current_offset = updateField(lpcs->dwCompReadAttrLen, - lpcs->dwCompReadAttrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadAttrLen, - &new_one->dwCompReadAttrOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadClauseLen, - lpcs->dwCompReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadClauseLen, - &new_one->dwCompReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadStrLen, - lpcs->dwCompReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadStrLen, - &new_one->dwCompReadStrOffset, TRUE); - - current_offset = updateField(lpcs->dwCompAttrLen, - lpcs->dwCompAttrOffset, - current_offset, newdata, olddata, - &new_one->dwCompAttrLen, - &new_one->dwCompAttrOffset, FALSE); - - current_offset = updateField(lpcs->dwCompClauseLen, - lpcs->dwCompClauseOffset, - current_offset, newdata, olddata, - &new_one->dwCompClauseLen, - &new_one->dwCompClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwCompStrLen, - lpcs->dwCompStrOffset, - current_offset, newdata, olddata, - &new_one->dwCompStrLen, - &new_one->dwCompStrOffset, TRUE); - - new_one->dwCursorPos = lpcs->dwCursorPos; - new_one->dwDeltaStart = 0; - - current_offset = updateField(lpcs->dwResultReadClauseLen, - lpcs->dwResultReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadClauseLen, - &new_one->dwResultReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwResultReadStrLen, - lpcs->dwResultReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadStrLen, - &new_one->dwResultReadStrOffset, TRUE); - - /* new ResultClause , ResultStr */ - - current_offset = updateField(lpcs->dwPrivateSize, - lpcs->dwPrivateOffset, - current_offset, newdata, olddata, - &new_one->dwPrivateSize, - &new_one->dwPrivateOffset, FALSE); - } - - /* set new data */ - /* ResultClause */ - if (len > 0) - { - new_one->dwResultClauseLen = sizeof(DWORD) * 2; - new_one->dwResultClauseOffset = current_offset; - *(DWORD*)&newdata[current_offset] = 0; - current_offset += sizeof(DWORD); - *(DWORD*)&newdata[current_offset] = len; - current_offset += sizeof(DWORD); - } - else - new_one->dwResultClauseLen = 0; - - /* ResultStr */ - new_one->dwResultStrLen = len; - if (len > 0) - { - new_one->dwResultStrOffset = current_offset; - memcpy(&newdata[current_offset], resultstr, len * sizeof(WCHAR)); - } - ImmUnlockIMCC(rc); - if (lpcs) - ImmUnlockIMCC(old); - - return rc; -} - static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam) { LPINPUTCONTEXT lpIMC; @@ -525,134 +218,8 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } -static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid) -{ - LPINPUTCONTEXT lpIMC; - DWORD flags = 0; - WCHAR wParam = 0; - LPIMEPRIVATE myPrivate; - BOOL sendMessage = TRUE; - - TRACE("(%p, %ld, %p, %ld):\n", hIMC, dwIndex, lpComp, dwCompLen); - - /* - * Explanation: - * this sets the composition string in the imm32.dll level - * of the composition buffer. - * TODO: set the Cocoa window's marked text string and tell text input context - */ - - lpIMC = LockRealIMC(hIMC); - - if (lpIMC == NULL) - return FALSE; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - - if (dwIndex == SCS_SETSTR) - { - HIMCC newCompStr; - - if (!myPrivate->bInComposition) - { - GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0); - myPrivate->bInComposition = TRUE; - } - - /* clear existing result */ - newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - flags = GCS_COMPSTR; - - if (dwCompLen && lpComp) - { - newCompStr = updateCompStr(lpIMC->hCompStr, (LPCWSTR)lpComp, dwCompLen / sizeof(WCHAR), &flags); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - wParam = ((const WCHAR*)lpComp)[0]; - flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART; - - if (cursor_valid) - { - LPCOMPOSITIONSTRING compstr; - compstr = ImmLockIMCC(lpIMC->hCompStr); - compstr->dwCursorPos = cursor_pos; - ImmUnlockIMCC(lpIMC->hCompStr); - flags |= GCS_CURSORPOS; - } - } - else - { - ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); - sendMessage = FALSE; - } - - } - - if (sendMessage) { - GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags); - ImmUnlockIMCC(lpIMC->hPrivate); - UnlockRealIMC(hIMC); - } - - return TRUE; -} - -static void IME_NotifyComplete(void* hIMC) -{ - ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); -} - /* Interfaces to other parts of the Mac driver */ -/*********************************************************************** - * macdrv_ime_set_text - */ -NTSTATUS WINAPI macdrv_ime_set_text(void *arg, ULONG size) -{ - struct ime_set_text_params *params = arg; - ULONG length = (size - offsetof(struct ime_set_text_params, text)) / sizeof(WCHAR); - void *himc = param_ptr(params->himc); - HWND hwnd = UlongToHandle(params->hwnd); - - if (!himc) himc = RealIMC(FROM_MACDRV); - - if (length) - { - if (himc) - IME_SetCompositionString(himc, SCS_SETSTR, params->text, length * sizeof(WCHAR), - params->cursor_pos, !params->complete); - else - { - RAWINPUT rawinput; - INPUT input; - unsigned int i; - - input.type = INPUT_KEYBOARD; - input.ki.wVk = 0; - input.ki.time = 0; - input.ki.dwExtraInfo = 0; - - for (i = 0; i < length; i++) - { - input.ki.wScan = params->text[i]; - input.ki.dwFlags = KEYEVENTF_UNICODE; - __wine_send_input(hwnd, &input, &rawinput); - - input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; - __wine_send_input(hwnd, &input, &rawinput); - } - } - } - - if (params->complete) - IME_NotifyComplete(himc); - return 0; -} - /************************************************************************** * macdrv_ime_query_char_rect */ diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 1a2bb684583..3dedd5e3043 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -28,6 +28,9 @@ #endif #include "macdrv_cocoa.h" + +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "ntgdi.h" diff --git a/dlls/winemac.drv/macdrv_dll.h b/dlls/winemac.drv/macdrv_dll.h index 3a11528eabc..8f13b6b69aa 100644 --- a/dlls/winemac.drv/macdrv_dll.h +++ b/dlls/winemac.drv/macdrv_dll.h @@ -31,7 +31,6 @@ extern NTSTATUS WINAPI macdrv_dnd_query_drag(void *arg, ULONG size) DECLSPEC_HID extern NTSTATUS WINAPI macdrv_dnd_query_drop(void *arg, ULONG size) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI macdrv_dnd_query_exited(void *arg, ULONG size) DECLSPEC_HIDDEN; -extern NTSTATUS WINAPI macdrv_ime_set_text(void *params, ULONG size) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI macdrv_ime_query_char_rect(void *params, ULONG size) DECLSPEC_HIDDEN; extern HMODULE macdrv_module DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 08c36860dd7..337caa6def2 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -92,7 +92,6 @@ enum macdrv_client_funcs client_func_dnd_query_drop, client_func_dnd_query_exited, client_func_ime_query_char_rect, - client_func_ime_set_text, client_func_last }; @@ -168,16 +167,6 @@ struct ime_query_char_rect_params UINT32 length; }; -/* macdrv_ime_set_text params */ -struct ime_set_text_params -{ - UINT32 hwnd; - UINT32 cursor_pos; - UINT32 himc; - UINT32 complete; - WCHAR text[1]; -}; - static inline void *param_ptr(UINT64 param) { return (void *)(UINT_PTR)param; From 759853b451ec7374a6602eca42dbd353f77d1d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 15:42:44 +0200 Subject: [PATCH 309/758] imm32: Update the IME composition window position after drawing. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53860 (cherry picked from commit bbfb15a5e5c20097be0bb882eb2800b77ba5c1b5) --- dlls/imm32/ime.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index c615e3406c9..b31f56f234f 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -186,7 +186,7 @@ static UINT ime_set_composition_status( HIMC himc, BOOL composition ) static void ime_ui_paint( HIMC himc, HWND hwnd ) { PAINTSTRUCT ps; - RECT rect; + RECT rect, new_rect; HDC hdc; HMONITOR monitor; MONITORINFO mon_info; @@ -201,6 +201,7 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) GetClientRect( hwnd, &rect ); FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + new_rect = rect; if ((str = input_context_get_comp_str( ctx, FALSE, &len ))) { @@ -233,6 +234,7 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) rect.top = cpt.y; rect.right = rect.left + pt.x; rect.bottom = rect.top + pt.y; + offset.x = offset.y = 0; monitor = MonitorFromPoint( cpt, MONITOR_DEFAULTTOPRIMARY ); } else /* CFS_DEFAULT */ @@ -283,8 +285,7 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) } } - SetWindowPos( hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, SWP_NOACTIVATE ); + new_rect = rect; TextOutW( hdc, offset.x, offset.y, str, len ); if (font) SelectObject( hdc, font ); @@ -293,6 +294,10 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) EndPaint( hwnd, &ps ); ImmUnlockIMC( himc ); + + if (!EqualRect( &rect, &new_rect )) + SetWindowPos( hwnd, HWND_TOPMOST, new_rect.left, new_rect.top, new_rect.right - new_rect.left, + new_rect.bottom - new_rect.top, SWP_NOACTIVATE ); } static void ime_ui_update_window( INPUTCONTEXT *ctx, HWND hwnd ) From b4a68d5c4017a246535b973076c08eac18cda96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 15:44:19 +0200 Subject: [PATCH 310/758] imm32: Use DrawTextW to wrap IME composition string. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53860 (cherry picked from commit 478ffa8c12d6c2635ff5493b0be40f9eac736376) --- dlls/imm32/ime.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index b31f56f234f..c10db8958f7 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -253,11 +253,10 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) if (ctx->cfCompForm.dwStyle == CFS_RECT) { - RECT client; - client = ctx->cfCompForm.rcArea; + RECT client = ctx->cfCompForm.rcArea; MapWindowPoints( ctx->hWnd, 0, (POINT *)&client, 2 ); IntersectRect( &rect, &rect, &client ); - /* TODO: Wrap the input if needed */ + DrawTextW( hdc, str, len, &rect, DT_WORDBREAK | DT_CALCRECT ); } if (ctx->cfCompForm.dwStyle == CFS_DEFAULT) @@ -286,7 +285,8 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) } new_rect = rect; - TextOutW( hdc, offset.x, offset.y, str, len ); + OffsetRect( &rect, offset.x - rect.left, offset.y - rect.top ); + DrawTextW( hdc, str, len, &rect, DT_WORDBREAK ); if (font) SelectObject( hdc, font ); free( str ); From c93efb99b670fa8deabf274352303be038dbd848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 15:12:01 +0200 Subject: [PATCH 311/758] user32/edit: Notify IME on position, format_rect and font changes. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53860 (cherry picked from commit 40fddc6233856674c133142f816c2161e52cba9a) --- dlls/user32/edit.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c index aa74d215673..19df11e1a8a 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -1779,6 +1779,20 @@ static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action) } +static void EDIT_UpdateImmCompositionWindow(EDITSTATE *es, UINT x, UINT y) +{ + COMPOSITIONFORM form = + { + .dwStyle = CFS_RECT, + .ptCurrentPos = {.x = x, .y = y}, + .rcArea = es->format_rect, + }; + HIMC himc = ImmGetContext(es->hwndSelf); + ImmSetCompositionWindow(himc, &form); + ImmReleaseContext(es->hwndSelf, himc); +} + + /********************************************************************* * * EDIT_SetCaretPos @@ -1794,6 +1808,7 @@ static void EDIT_SetCaretPos(EDITSTATE *es, INT pos, res = EDIT_EM_PosFromChar(es, pos, after_wrap); TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res)); SetCaretPos((short)LOWORD(res), (short)HIWORD(res)); + EDIT_UpdateImmCompositionWindow(es, (short)LOWORD(res), (short)HIWORD(res)); } } @@ -3841,6 +3856,15 @@ static DWORD get_font_margins(HDC hdc, const TEXTMETRICW *tm, BOOL unicode) return MAKELONG(left, right); } +static void EDIT_UpdateImmCompositionFont(EDITSTATE *es) +{ + LOGFONTW composition_font; + HIMC himc = ImmGetContext(es->hwndSelf); + GetObjectW(es->font, sizeof(LOGFONTW), &composition_font); + ImmSetCompositionFontW(himc, &composition_font); + ImmReleaseContext(es->hwndSelf, himc); +} + /********************************************************************* * * WM_SETFONT @@ -3892,6 +3916,8 @@ static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw) es->flags & EF_AFTER_WRAP); NtUserShowCaret( es->hwndSelf ); } + + EDIT_UpdateImmCompositionFont(es); } @@ -4687,6 +4713,7 @@ LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, B { EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW( hwnd, 0 ); LRESULT result = 0; + POINT pt; TRACE("hwnd=%p msg=%x (%s) wparam=%Ix lparam=%Ix\n", hwnd, msg, SPY_GetMsgName(msg, hwnd), wParam, lParam); @@ -5198,6 +5225,9 @@ LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, B /* IME messages to make the edit control IME aware */ case WM_IME_SETCONTEXT: + NtUserGetCaretPos(&pt); + EDIT_UpdateImmCompositionWindow(es, pt.x, pt.y); + EDIT_UpdateImmCompositionFont(es); break; case WM_IME_STARTCOMPOSITION: From fc3c83c0ca7993784d3d25f9ef9e6b12d9158955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 4 May 2023 20:54:44 +0200 Subject: [PATCH 312/758] user32/edit: Delegate composition string rendering to the IME UI. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53860 (cherry picked from commit 851c57d47506640f00399933d7411b62891157f3) --- dlls/user32/edit.c | 167 ++++----------------------------------------- 1 file changed, 13 insertions(+), 154 deletions(-) diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c index 19df11e1a8a..73df9d0b330 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -128,9 +128,8 @@ typedef struct /* * IME Data */ - UINT composition_len; /* length of composition, 0 == no composition */ - int composition_start; /* the character position for the composition */ - UINT ime_status; /* IME status flag */ + UINT ime_status; /* IME status flag */ + /* * Uniscribe Data */ @@ -2149,9 +2148,6 @@ static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col { COLORREF BkColor; COLORREF TextColor; - LOGFONTW underline_font; - HFONT hUnderline = 0; - HFONT old_font = 0; INT ret; INT li; INT BkMode; @@ -2163,20 +2159,9 @@ static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col BkColor = GetBkColor(dc); TextColor = GetTextColor(dc); if (rev) { - if (es->composition_len == 0) - { - SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT)); - SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode( dc, OPAQUE); - } - else - { - HFONT current = GetCurrentObject(dc,OBJ_FONT); - GetObjectW(current,sizeof(LOGFONTW),&underline_font); - underline_font.lfUnderline = TRUE; - hUnderline = CreateFontIndirectW(&underline_font); - old_font = SelectObject(dc,hUnderline); - } + SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT)); + SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkMode(dc, OPAQUE); } li = EDIT_EM_LineIndex(es, line); if (es->style & ES_MULTILINE) { @@ -2188,19 +2173,9 @@ static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col ret = size.cx; } if (rev) { - if (es->composition_len == 0) - { - SetBkColor(dc, BkColor); - SetTextColor(dc, TextColor); - SetBkMode( dc, BkMode); - } - else - { - if (old_font) - SelectObject(dc,old_font); - if (hUnderline) - DeleteObject(hUnderline); - } + SetBkColor(dc, BkColor); + SetTextColor(dc, TextColor); + SetBkMode(dc, BkMode); } return ret; } @@ -4362,75 +4337,6 @@ static LRESULT EDIT_EM_GetThumb(EDITSTATE *es) * The Following code is to handle inline editing from IMEs */ -static void EDIT_GetCompositionStr(HIMC hIMC, LPARAM CompFlag, EDITSTATE *es) -{ - LONG buflen; - LPWSTR lpCompStr; - LPSTR lpCompStrAttr = NULL; - DWORD dwBufLenAttr; - - buflen = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); - - if (buflen < 0) - { - return; - } - - lpCompStr = HeapAlloc(GetProcessHeap(),0,buflen); - if (!lpCompStr) - { - ERR("Unable to allocate IME CompositionString\n"); - return; - } - - if (buflen) - ImmGetCompositionStringW(hIMC, GCS_COMPSTR, lpCompStr, buflen); - - if (CompFlag & GCS_COMPATTR) - { - /* - * We do not use the attributes yet. it would tell us what characters - * are in transition and which are converted or decided upon - */ - dwBufLenAttr = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0); - if (dwBufLenAttr) - { - dwBufLenAttr ++; - lpCompStrAttr = HeapAlloc(GetProcessHeap(),0,dwBufLenAttr+1); - if (!lpCompStrAttr) - { - ERR("Unable to allocate IME Attribute String\n"); - HeapFree(GetProcessHeap(),0,lpCompStr); - return; - } - ImmGetCompositionStringW(hIMC,GCS_COMPATTR, lpCompStrAttr, - dwBufLenAttr); - lpCompStrAttr[dwBufLenAttr] = 0; - } - } - - /* check for change in composition start */ - if (es->selection_end < es->composition_start) - es->composition_start = es->selection_end; - - /* replace existing selection string */ - es->selection_start = es->composition_start; - - if (es->composition_len > 0) - es->selection_end = es->composition_start + es->composition_len; - else - es->selection_end = es->selection_start; - - EDIT_EM_ReplaceSel(es, FALSE, lpCompStr, buflen / sizeof(WCHAR), TRUE, TRUE); - es->composition_len = abs(es->composition_start - es->selection_end); - - es->selection_start = es->composition_start; - es->selection_end = es->selection_start + es->composition_len; - - HeapFree(GetProcessHeap(),0,lpCompStrAttr); - HeapFree(GetProcessHeap(),0,lpCompStr); -} - static void EDIT_GetResultStr(HIMC hIMC, EDITSTATE *es) { LONG buflen; @@ -4450,48 +4356,22 @@ static void EDIT_GetResultStr(HIMC hIMC, EDITSTATE *es) } ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, lpResultStr, buflen); - - /* check for change in composition start */ - if (es->selection_end < es->composition_start) - es->composition_start = es->selection_end; - - es->selection_start = es->composition_start; - es->selection_end = es->composition_start + es->composition_len; EDIT_EM_ReplaceSel(es, TRUE, lpResultStr, buflen / sizeof(WCHAR), TRUE, TRUE); - es->composition_start = es->selection_end; - es->composition_len = 0; - HeapFree(GetProcessHeap(),0,lpResultStr); } static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es) { HIMC hIMC; - int cursor; - - if (es->composition_len == 0 && es->selection_start != es->selection_end) - { - EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); - es->composition_start = es->selection_end; - } hIMC = ImmGetContext(hwnd); if (!hIMC) return; if (CompFlag & GCS_RESULTSTR) - { EDIT_GetResultStr(hIMC, es); - cursor = 0; - } - else - { - if (CompFlag & GCS_COMPSTR) - EDIT_GetCompositionStr(hIMC, CompFlag, es); - cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, 0, 0); - } + ImmReleaseContext(hwnd, hIMC); - EDIT_SetCaretPos(es, es->selection_start + cursor, es->flags & EF_AFTER_WRAP); } @@ -5230,32 +5110,11 @@ LRESULT EditWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, B EDIT_UpdateImmCompositionFont(es); break; - case WM_IME_STARTCOMPOSITION: - es->composition_start = es->selection_end; - es->composition_len = 0; - break; - case WM_IME_COMPOSITION: - if (lParam & GCS_RESULTSTR && !(es->ime_status & EIMES_GETCOMPSTRATONCE)) - { - DefWindowProcT(hwnd, msg, wParam, lParam, unicode); - break; - } - - EDIT_ImeComposition(hwnd, lParam, es); - break; - - case WM_IME_ENDCOMPOSITION: - if (es->composition_len > 0) - { - EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); - es->selection_end = es->selection_start; - es->composition_len= 0; - } - break; - - case WM_IME_COMPOSITIONFULL: - break; + EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); + if ((lParam & GCS_RESULTSTR) && (es->ime_status & EIMES_GETCOMPSTRATONCE)) + EDIT_ImeComposition(hwnd, lParam, es); + return DefWindowProcW(hwnd, msg, wParam, lParam); case WM_IME_SELECT: break; From 376ef0a6dfa8014072562da10c80158e96a7e164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 21:43:58 +0200 Subject: [PATCH 313/758] winemac: Return the caret position in query_ime_char_rect. And drop the ime_query_char_rect driver user callback. (cherry picked from commit 6a7c131d0ec87cf1719868be22ff5054573f4c48) --- dlls/winemac.drv/dllmain.c | 1 - dlls/winemac.drv/event.c | 22 +++-- dlls/winemac.drv/ime.c | 147 ---------------------------------- dlls/winemac.drv/macdrv_dll.h | 2 - dlls/winemac.drv/unixlib.h | 19 ----- 5 files changed, 9 insertions(+), 182 deletions(-) diff --git a/dlls/winemac.drv/dllmain.c b/dlls/winemac.drv/dllmain.c index 265d6e46d5c..b20f8d71dc9 100644 --- a/dlls/winemac.drv/dllmain.c +++ b/dlls/winemac.drv/dllmain.c @@ -374,7 +374,6 @@ static const kernel_callback kernel_callbacks[] = macdrv_dnd_query_drag, macdrv_dnd_query_drop, macdrv_dnd_query_exited, - macdrv_ime_query_char_rect, }; C_ASSERT(NtUserDriverCallbackFirst + ARRAYSIZE(kernel_callbacks) == client_func_last); diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 1565cd9cbdd..11f5dd3314c 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -401,25 +401,21 @@ BOOL query_ime_char_rect(macdrv_query* query) HWND hwnd = macdrv_get_window_hwnd(query->window); void *himc = query->ime_char_rect.himc; CFRange *range = &query->ime_char_rect.range; - CGRect *rect = &query->ime_char_rect.rect; - struct ime_query_char_rect_result result = { .location = 0 }; - struct ime_query_char_rect_params params; - BOOL ret; + GUITHREADINFO info = {.cbSize = sizeof(info)}; + BOOL ret = FALSE; TRACE_(imm)("win %p/%p himc %p range %ld-%ld\n", hwnd, query->window, himc, range->location, range->length); - params.hwnd = HandleToUlong(hwnd); - params.himc = (UINT_PTR)himc; - params.result = (UINT_PTR)&result; - params.location = range->location; - params.length = range->length; - ret = macdrv_client_func(client_func_ime_query_char_rect, ¶ms, sizeof(params)); - *range = CFRangeMake(result.location, result.length); - *rect = cgrect_from_rect(result.rect); + if (NtUserGetGUIThreadInfo(0, &info)) + { + NtUserMapWindowPoints(info.hwndCaret, 0, (POINT*)&info.rcCaret, 2); + if (range->length && info.rcCaret.left == info.rcCaret.right) info.rcCaret.right++; + query->ime_char_rect.rect = cgrect_from_rect(info.rcCaret); + } TRACE_(imm)(" -> %s range %ld-%ld rect %s\n", ret ? "TRUE" : "FALSE", range->location, - range->length, wine_dbgstr_cgrect(*rect)); + range->length, wine_dbgstr_cgrect(query->ime_char_rect.rect)); return ret; } diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index 87adab88876..ca54c34714c 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -47,47 +47,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); static HIMC *hSelectedFrom = NULL; static INT hSelectedCount = 0; -static WCHAR *input_context_get_comp_str( INPUTCONTEXT *ctx, BOOL result, UINT *length ) -{ - COMPOSITIONSTRING *string; - WCHAR *text = NULL; - UINT len, off; - - if (!(string = ImmLockIMCC( ctx->hCompStr ))) return NULL; - len = result ? string->dwResultStrLen : string->dwCompStrLen; - off = result ? string->dwResultStrOffset : string->dwCompStrOffset; - - if (len && off && (text = malloc( (len + 1) * sizeof(WCHAR) ))) - { - memcpy( text, (BYTE *)string + off, len * sizeof(WCHAR) ); - text[len] = 0; - *length = len; - } - - ImmUnlockIMCC( ctx->hCompStr ); - return text; -} - -static HWND input_context_get_ui_hwnd( INPUTCONTEXT *ctx ) -{ - struct ime_private *priv; - HWND hwnd; - if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; - hwnd = priv->hwndDefault; - ImmUnlockIMCC( ctx->hPrivate ); - return hwnd; -} - -static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) -{ - struct ime_private *priv; - HFONT font = NULL; - if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; - if (priv->textfont) font = SelectObject( hdc, priv->textfont ); - ImmUnlockIMCC( ctx->hPrivate ); - return font; -} - static HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_MACDRV) @@ -217,109 +176,3 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } - -/* Interfaces to other parts of the Mac driver */ - -/************************************************************************** - * macdrv_ime_query_char_rect - */ -NTSTATUS WINAPI macdrv_ime_query_char_rect(void *arg, ULONG size) -{ - struct ime_query_char_rect_params *params = arg; - struct ime_query_char_rect_result *result = param_ptr(params->result); - void *himc = param_ptr(params->himc); - IMECHARPOSITION charpos; - BOOL ret = FALSE; - - result->location = params->location; - result->length = params->length; - - if (!himc) himc = RealIMC(FROM_MACDRV); - - charpos.dwSize = sizeof(charpos); - charpos.dwCharPos = params->location; - if (ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos)) - { - int i; - - SetRect(&result->rect, charpos.pt.x, charpos.pt.y, 0, charpos.pt.y + charpos.cLineHeight); - - /* iterate over rest of length to extend rect */ - for (i = 1; i < params->length; i++) - { - charpos.dwSize = sizeof(charpos); - charpos.dwCharPos = params->location + i; - if (!ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos) || - charpos.pt.y != result->rect.top) - { - result->length = i; - break; - } - - result->rect.right = charpos.pt.x; - } - - ret = TRUE; - } - - if (!ret) - { - LPINPUTCONTEXT ic = ImmLockIMC(himc); - - if (ic) - { - WCHAR *str; - HWND hwnd; - UINT len; - - if ((hwnd = input_context_get_ui_hwnd( ic )) && IsWindowVisible( hwnd ) && - (str = input_context_get_comp_str( ic, FALSE, &len ))) - { - HDC dc = GetDC( hwnd ); - HFONT font = input_context_select_ui_font( ic, dc ); - SIZE size; - - if (result->location > len) result->location = len; - if (result->location + result->length > len) result->length = len - result->location; - - GetTextExtentPoint32W( dc, str, result->location, &size ); - charpos.rcDocument.left = size.cx; - charpos.rcDocument.top = 0; - GetTextExtentPoint32W( dc, str, result->location + result->length, &size ); - charpos.rcDocument.right = size.cx; - charpos.rcDocument.bottom = size.cy; - - if (ic->cfCompForm.dwStyle == CFS_DEFAULT) - OffsetRect(&charpos.rcDocument, 10, 10); - - LPtoDP(dc, (POINT*)&charpos.rcDocument, 2); - MapWindowPoints( hwnd, 0, (POINT *)&charpos.rcDocument, 2 ); - result->rect = charpos.rcDocument; - ret = TRUE; - - if (font) SelectObject( dc, font ); - ReleaseDC( hwnd, dc ); - free( str ); - } - } - - ImmUnlockIMC(himc); - } - - if (!ret) - { - GUITHREADINFO gti; - gti.cbSize = sizeof(gti); - if (GetGUIThreadInfo(0, >i)) - { - MapWindowPoints(gti.hwndCaret, 0, (POINT*)>i.rcCaret, 2); - result->rect = gti.rcCaret; - ret = TRUE; - } - } - - if (ret && result->length && result->rect.left == result->rect.right) - result->rect.right++; - - return ret; -} diff --git a/dlls/winemac.drv/macdrv_dll.h b/dlls/winemac.drv/macdrv_dll.h index 8f13b6b69aa..1bbc7dfc7d6 100644 --- a/dlls/winemac.drv/macdrv_dll.h +++ b/dlls/winemac.drv/macdrv_dll.h @@ -31,8 +31,6 @@ extern NTSTATUS WINAPI macdrv_dnd_query_drag(void *arg, ULONG size) DECLSPEC_HID extern NTSTATUS WINAPI macdrv_dnd_query_drop(void *arg, ULONG size) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI macdrv_dnd_query_exited(void *arg, ULONG size) DECLSPEC_HIDDEN; -extern NTSTATUS WINAPI macdrv_ime_query_char_rect(void *params, ULONG size) DECLSPEC_HIDDEN; - extern HMODULE macdrv_module DECLSPEC_HIDDEN; #endif /* __WINE_MACDRV_DLL_H */ diff --git a/dlls/winemac.drv/unixlib.h b/dlls/winemac.drv/unixlib.h index 337caa6def2..61f4f44fb75 100644 --- a/dlls/winemac.drv/unixlib.h +++ b/dlls/winemac.drv/unixlib.h @@ -91,7 +91,6 @@ enum macdrv_client_funcs client_func_dnd_query_drag, client_func_dnd_query_drop, client_func_dnd_query_exited, - client_func_ime_query_char_rect, client_func_last }; @@ -149,24 +148,6 @@ struct dnd_query_exited_params UINT32 hwnd; }; -/* macdrv_ime_query_char_rect result */ -struct ime_query_char_rect_result -{ - RECT rect; - UINT32 location; - UINT32 length; -}; - -/* macdrv_ime_query_char_rect params */ -struct ime_query_char_rect_params -{ - UINT32 hwnd; - UINT32 location; - UINT32 himc; - UINT64 result; /* FIXME: Use NtCallbackReturn instead */ - UINT32 length; -}; - static inline void *param_ptr(UINT64 param) { return (void *)(UINT_PTR)param; From 4018278f4cbf68201d9db8454d7d291ec8bcd91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 21:54:13 +0200 Subject: [PATCH 314/758] winemac: Remove now unnecessary selected HIMC tracking code. (cherry picked from commit 70ff82312e562578d0a98285df12fa32d1a3144f) --- dlls/winemac.drv/ime.c | 92 ++++-------------------------------------- 1 file changed, 7 insertions(+), 85 deletions(-) diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index ca54c34714c..5564203b78a 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -42,51 +42,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); -#define FROM_MACDRV ((HIMC)0xcafe1337) - -static HIMC *hSelectedFrom = NULL; -static INT hSelectedCount = 0; - -static HIMC RealIMC(HIMC hIMC) -{ - if (hIMC == FROM_MACDRV) - { - INT i; - HWND wnd = GetFocus(); - HIMC winHimc = ImmGetContext(wnd); - for (i = 0; i < hSelectedCount; i++) - if (winHimc == hSelectedFrom[i]) - return winHimc; - return NULL; - } - else - return hIMC; -} - -static LPINPUTCONTEXT LockRealIMC(HIMC hIMC) -{ - HIMC real_imc = RealIMC(hIMC); - if (real_imc) - return ImmLockIMC(real_imc); - else - return NULL; -} - -static BOOL UnlockRealIMC(HIMC hIMC) -{ - HIMC real_imc = RealIMC(hIMC); - if (real_imc) - return ImmUnlockIMC(real_imc); - else - return FALSE; -} - static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam) { LPINPUTCONTEXT lpIMC; LPTRANSMSG lpTransMsg; - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC == NULL) return; @@ -106,34 +67,8 @@ static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam ImmUnlockIMCC(lpIMC->hMsgBuf); lpIMC->dwNumMsgBuf++; - ImmGenerateMessage(RealIMC(hIMC)); - UnlockRealIMC(hIMC); -} - -static BOOL IME_RemoveFromSelected(HIMC hIMC) -{ - int i; - for (i = 0; i < hSelectedCount; i++) - { - if (hSelectedFrom[i] == hIMC) - { - if (i < hSelectedCount - 1) - memmove(&hSelectedFrom[i], &hSelectedFrom[i + 1], (hSelectedCount - i - 1) * sizeof(HIMC)); - hSelectedCount--; - return TRUE; - } - } - return FALSE; -} - -static void IME_AddToSelected(HIMC hIMC) -{ - hSelectedCount++; - if (hSelectedFrom) - hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount * sizeof(HIMC)); - else - hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC)); - hSelectedFrom[hSelectedCount - 1] = hIMC; + ImmGenerateMessage(hIMC); + ImmUnlockIMC(hIMC); } BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) @@ -141,23 +76,10 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) LPINPUTCONTEXT lpIMC; TRACE("%p %s\n", hIMC, fSelect ? "TRUE" : "FALSE"); - if (hIMC == FROM_MACDRV) - { - ERR("ImeSelect should never be called from Cocoa\n"); - return FALSE; - } - - if (!hIMC) - return TRUE; - - /* not selected */ - if (!fSelect) - return IME_RemoveFromSelected(hIMC); - - IME_AddToSelected(hIMC); + if (!hIMC || !fSelect) return TRUE; /* Initialize our structures */ - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC != NULL) { LPIMEPRIVATE myPrivate; @@ -165,13 +87,13 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) if (myPrivate->bInComposition) GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); if (myPrivate->bInternalState) - ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); + ImmSetOpenStatus( hIMC, FALSE ); myPrivate->bInComposition = FALSE; myPrivate->bInternalState = FALSE; myPrivate->textfont = NULL; myPrivate->hwndDefault = NULL; ImmUnlockIMCC(lpIMC->hPrivate); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } return TRUE; From ecbf08c6f26908be88fcadc20f1de94a5064e3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 12 May 2023 07:45:40 +0200 Subject: [PATCH 315/758] winemac: Use the default IME implementation for ImeSelect. (cherry picked from commit b3fbc16feaf4119be9ce7f20848995fb2388f489) --- dlls/imm32/ime.c | 18 +++++- dlls/winemac.drv/Makefile.in | 1 - dlls/winemac.drv/ime.c | 100 ------------------------------ dlls/winemac.drv/winemac.drv.spec | 3 - 4 files changed, 17 insertions(+), 105 deletions(-) delete mode 100644 dlls/winemac.drv/ime.c diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index c10db8958f7..f5799440709 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -433,7 +433,23 @@ BOOL WINAPI ImeDestroy( UINT force ) BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) { - FIXME( "himc %p, select %d semi-stub!\n", himc, select ); + struct ime_private *priv; + INPUTCONTEXT *ctx; + + TRACE( "himc %p, select %u\n", himc, select ); + + if (!himc || !select) return TRUE; + if (!(ctx = ImmLockIMC( himc ))) return FALSE; + + ImmSetOpenStatus( himc, FALSE ); + + if ((priv = ImmLockIMCC( ctx->hPrivate ))) + { + memset( priv, 0, sizeof(*priv) ); + ImmUnlockIMCC( ctx->hPrivate ); + } + + ImmUnlockIMC( himc ); return TRUE; } diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index 9735890b221..7228dfbac4f 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -12,7 +12,6 @@ C_SRCS = \ event.c \ gdi.c \ image.c \ - ime.c \ keyboard.c \ macdrv_main.c \ mouse.c \ diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c deleted file mode 100644 index 5564203b78a..00000000000 --- a/dlls/winemac.drv/ime.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * The IME for interfacing with Mac input methods - * - * Copyright 2008, 2013 CodeWeavers, Aric Stewart - * Copyright 2013 Ken Thomases for CodeWeavers Inc. - * - * 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 - */ - -/* - * Notes: - * The normal flow for IMM/IME Processing is as follows. - * 1) The Keyboard Driver generates key messages which are first passed to - * the IMM and then to IME via ImeProcessKey. If the IME returns 0 then - * it does not want the key and the keyboard driver then generates the - * WM_KEYUP/WM_KEYDOWN messages. However if the IME is going to process the - * key it returns non-zero. - * 2) If the IME is going to process the key then the IMM calls ImeToAsciiEx to - * process the key. the IME modifies the HIMC structure to reflect the - * current state and generates any messages it needs the IMM to process. - * 3) IMM checks the messages and send them to the application in question. From - * here the IMM level deals with if the application is IME aware or not. - */ - -#include "macdrv_dll.h" -#include "imm.h" -#include "immdev.h" -#include "wine/server.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(imm); - -static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam) -{ - LPINPUTCONTEXT lpIMC; - LPTRANSMSG lpTransMsg; - - lpIMC = ImmLockIMC(hIMC); - if (lpIMC == NULL) - return; - - lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG)); - if (!lpIMC->hMsgBuf) - return; - - lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); - if (!lpTransMsg) - return; - - lpTransMsg += lpIMC->dwNumMsgBuf; - lpTransMsg->message = msg; - lpTransMsg->wParam = wParam; - lpTransMsg->lParam = lParam; - - ImmUnlockIMCC(lpIMC->hMsgBuf); - lpIMC->dwNumMsgBuf++; - - ImmGenerateMessage(hIMC); - ImmUnlockIMC(hIMC); -} - -BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) -{ - LPINPUTCONTEXT lpIMC; - TRACE("%p %s\n", hIMC, fSelect ? "TRUE" : "FALSE"); - - if (!hIMC || !fSelect) return TRUE; - - /* Initialize our structures */ - lpIMC = ImmLockIMC(hIMC); - if (lpIMC != NULL) - { - LPIMEPRIVATE myPrivate; - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - if (myPrivate->bInComposition) - GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); - if (myPrivate->bInternalState) - ImmSetOpenStatus( hIMC, FALSE ); - myPrivate->bInComposition = FALSE; - myPrivate->bInternalState = FALSE; - myPrivate->textfont = NULL; - myPrivate->hwndDefault = NULL; - ImmUnlockIMCC(lpIMC->hPrivate); - ImmUnlockIMC(hIMC); - } - - return TRUE; -} diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 68bebc794aa..5f086f5c4e5 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -1,5 +1,2 @@ # System tray @ cdecl wine_notify_icon(long ptr) - -# IME -@ stdcall ImeSelect(long long) From 6409031c67c0a4b1d81afb169f029fe90f0e5850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 5 May 2023 09:15:23 +0200 Subject: [PATCH 316/758] winex11: Use ime_comp_buf != NULL instead of ximInComposeMode. (cherry picked from commit e70b1b722aebf22f52b90f9f00e9ea37935de752) --- dlls/winex11.drv/event.c | 4 +--- dlls/winex11.drv/x11drv.h | 2 +- dlls/winex11.drv/xim.c | 12 ++++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 2aaf8e482db..adcf286546d 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -49,8 +49,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(event); WINE_DECLARE_DEBUG_CHANNEL(xdnd); -extern BOOL ximInComposeMode; - #define DndNotDnd -1 /* OffiX drag&drop */ #define DndUnknown 0 #define DndRawData 1 @@ -887,7 +885,7 @@ static void focus_out( Display *display , HWND hwnd ) XIC xic; struct x11drv_win_data *data; - if (ximInComposeMode) return; + if (xim_in_compose_mode()) return; data = get_win_data(hwnd); if(data){ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 3bfe28f39a4..8d3f75a2a2e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -439,7 +439,6 @@ extern BOOL clipping_cursor DECLSPEC_HIDDEN; extern unsigned int screen_bpp DECLSPEC_HIDDEN; extern BOOL usexrandr DECLSPEC_HIDDEN; extern BOOL usexvidmode DECLSPEC_HIDDEN; -extern BOOL ximInComposeMode DECLSPEC_HIDDEN; extern BOOL use_take_focus DECLSPEC_HIDDEN; extern BOOL use_primary_selection DECLSPEC_HIDDEN; extern BOOL use_system_cursors DECLSPEC_HIDDEN; @@ -878,6 +877,7 @@ extern struct x11drv_display_device_handler desktop_handler DECLSPEC_HIDDEN; /* XIM support */ extern BOOL xim_init( const WCHAR *input_style ) DECLSPEC_HIDDEN; extern void xim_thread_attach( struct x11drv_thread_data *data ) DECLSPEC_HIDDEN; +extern BOOL xim_in_compose_mode(void) DECLSPEC_HIDDEN; extern void X11DRV_XIMLookupChars( const char *str, UINT count ) DECLSPEC_HIDDEN; extern XIC X11DRV_get_ic( HWND hwnd ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index b7ab69029b9..9f7efd78f84 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -42,8 +42,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(xim); #define XICProc XIMProc #endif -BOOL ximInComposeMode=FALSE; - static WCHAR *ime_comp_buf; static XIMStyle input_style = 0; @@ -69,6 +67,11 @@ static const char *debugstr_xim_style( XIMStyle style ) return wine_dbg_sprintf( "%s", buffer ); } +BOOL xim_in_compose_mode(void) +{ + return !!ime_comp_buf; +} + static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len ) { UINT len = ime_comp_buf ? wcslen( ime_comp_buf ) : 0; @@ -138,7 +141,9 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); x11drv_client_call( client_ime_set_composition_status, TRUE ); - ximInComposeMode = TRUE; + if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0; + else ERR( "Failed to allocate preedit buffer\n" ); + return -1; } @@ -148,7 +153,6 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); - ximInComposeMode = FALSE; free( ime_comp_buf ); ime_comp_buf = NULL; From 8a34a11fdce22a0db4813e04ec7fdf065490a792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 7 May 2023 22:16:08 +0200 Subject: [PATCH 317/758] winex11: Keep track of the cursor position on the XIM side. The caret callback is rarely used and XIM also doesn't support changing the preedit string, so we cannot support composition string updates from the PE side either. Requesting only the cursor position, is then likely not useful. (cherry picked from commit 5d0526d29847325de24c99b75d833fe393acfbef) --- dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 20 -------------------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 5 +++-- 5 files changed, 3 insertions(+), 25 deletions(-) diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 500a4a6bc44..858c8dc4ee4 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -30,7 +30,6 @@ static const callback_func callback_funcs[] = { x11drv_dnd_drop_event, x11drv_dnd_leave_event, - x11drv_ime_get_cursor_pos, x11drv_ime_set_composition_status, x11drv_ime_set_cursor_pos, x11drv_ime_set_open_status, diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index b375b7f8e2a..37e84ec4f6b 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -550,26 +550,6 @@ NTSTATUS x11drv_ime_set_composition_status( UINT open ) return 0; } -NTSTATUS x11drv_ime_get_cursor_pos( UINT arg ) -{ - LPINPUTCONTEXT lpIMC; - INT rc = 0; - LPCOMPOSITIONSTRING compstr; - - if (!hSelectedFrom) - return rc; - - lpIMC = LockRealIMC(FROM_X11); - if (lpIMC) - { - compstr = ImmLockIMCC(lpIMC->hCompStr); - rc = compstr->dwCursorPos; - ImmUnlockIMCC(lpIMC->hCompStr); - } - UnlockRealIMC(FROM_X11); - return rc; -} - NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) { LPINPUTCONTEXT lpIMC; diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 20279bdb2ac..8cd35583f5d 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -94,7 +94,6 @@ enum client_callback { client_dnd_drop_event, client_dnd_leave_event, - client_ime_get_cursor_pos, client_ime_set_composition_status, client_ime_set_cursor_pos, client_ime_set_open_status, diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 047bb430d39..6740609f9ab 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -36,7 +36,6 @@ extern NTSTATUS WINAPI x11drv_systray_change_owner( void *params, ULONG size ) D extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_get_cursor_pos( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_open_status( UINT open ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 9f7efd78f84..2f3886f1095 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -201,6 +201,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) { + static int xim_caret_pos; XIMPreeditCaretCallbackStruct *params = (void *)arg; HWND hwnd = (HWND)user; int pos; @@ -209,7 +210,7 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) if (!params) return 0; - pos = x11drv_client_call( client_ime_get_cursor_pos, 0 ); + pos = xim_caret_pos; switch (params->direction) { case XIMForwardChar: @@ -238,7 +239,7 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) break; } x11drv_client_call( client_ime_set_cursor_pos, pos ); - params->position = pos; + params->position = xim_caret_pos = pos; return 0; } From bc9a53c3cc2b6383a265090ee07492a991c17159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 11:56:58 +0200 Subject: [PATCH 318/758] winex11: Send an internal WM_IME_NOTIFY wparam to set open status. (cherry picked from commit 4e92c5722160fc17fbb8aedf0e7560d64179339a) --- dlls/imm32/ime.c | 19 ++++++++++++++++--- dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 9 --------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 6 ++---- include/ntuser.h | 3 +++ 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index f5799440709..46f12ef6f45 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -40,6 +40,7 @@ static const char *debugstr_imn( WPARAM wparam ) case IMN_SETCOMPOSITIONWINDOW: return "IMN_SETCOMPOSITIONWINDOW"; case IMN_GUIDELINE: return "IMN_GUIDELINE"; case IMN_SETSTATUSWINDOWPOS: return "IMN_SETSTATUSWINDOWPOS"; + case IMN_WINE_SET_OPEN_STATUS: return "IMN_WINE_SET_OPEN_STATUS"; default: return wine_dbg_sprintf( "%#Ix", wparam ); } } @@ -337,6 +338,20 @@ static void ime_ui_start_composition( HIMC himc, HWND hwnd ) ImmUnlockIMC( himc ); } +static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, WPARAM wparam, LPARAM lparam ) +{ + TRACE( "himc %p, hwnd %p, wparam %s, lparam %#Ix\n", hwnd, himc, debugstr_imn(wparam), lparam ); + + switch (wparam) + { + case IMN_WINE_SET_OPEN_STATUS: + return ImmSetOpenStatus( himc, lparam ); + default: + FIXME( "himc %p, hwnd %p, wparam %s, lparam %#Ix stub!\n", hwnd, himc, debugstr_imn(wparam), lparam ); + return 0; + } +} + static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); @@ -379,9 +394,7 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP ShowWindow( hwnd, SW_HIDE ); break; case WM_IME_NOTIFY: - FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc, - debugstr_wm_ime(msg), debugstr_imn(wparam), lparam ); - return 0; + return ime_ui_notify( himc, hwnd, wparam, lparam ); case WM_IME_CONTROL: FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc, debugstr_wm_ime(msg), debugstr_imc(wparam), lparam ); diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 858c8dc4ee4..a3375e60957 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -32,7 +32,6 @@ static const callback_func callback_funcs[] = x11drv_dnd_leave_event, x11drv_ime_set_composition_status, x11drv_ime_set_cursor_pos, - x11drv_ime_set_open_status, x11drv_ime_update_association, }; diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 37e84ec4f6b..11832996084 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -516,15 +516,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) /* Interfaces to XIM and other parts of winex11drv */ -NTSTATUS x11drv_ime_set_open_status( UINT open ) -{ - HIMC imc; - - imc = RealIMC(FROM_X11); - ImmSetOpenStatus(imc, open); - return 0; -} - NTSTATUS x11drv_ime_set_composition_status( UINT open ) { HIMC imc; diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 8cd35583f5d..ef965381479 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -96,7 +96,6 @@ enum client_callback client_dnd_leave_event, client_ime_set_composition_status, client_ime_set_cursor_pos, - client_ime_set_open_status, client_ime_update_association, client_funcs_count }; diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 6740609f9ab..2cc27abd362 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -38,7 +38,6 @@ extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_set_open_status( UINT open ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN; extern LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 2f3886f1095..0d12b155da4 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -122,12 +122,10 @@ static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg ) switch (state) { case XIMPreeditEnable: - x11drv_client_call( client_ime_set_open_status, TRUE ); + send_message( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE ); break; case XIMPreeditDisable: - x11drv_client_call( client_ime_set_open_status, FALSE ); - break; - default: + send_message( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE ); break; } diff --git a/include/ntuser.h b/include/ntuser.h index 45082f67dfb..c8a3133a36a 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -492,6 +492,9 @@ enum wine_internal_message #define IME_INTERNAL_HKL_ACTIVATE 0x19 #define IME_INTERNAL_HKL_DEACTIVATE 0x20 +/* internal WM_IME_NOTIFY wparams, not compatible with Windows */ +#define IMN_WINE_SET_OPEN_STATUS 0x000f + /* builtin IME driver calls */ enum wine_ime_call { From 42052d9436286fcfdd6bcb9c8d1d91c5d1a2165d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 19 May 2023 10:29:22 +0200 Subject: [PATCH 319/758] winex11: Set or clear XIC focus using a xim_set_focus helper. (cherry picked from commit ecd8c9310ff9e6fd15ee9e174729655766d5218a) --- dlls/winex11.drv/event.c | 7 +++---- dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/xim.c | 10 ++++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index adcf286546d..e760986807c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -816,7 +816,6 @@ static const char * const focus_modes[] = static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) { XFocusChangeEvent *event = &xev->xfocus; - XIC xic; if (!hwnd) return FALSE; @@ -853,7 +852,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; - if ((xic = X11DRV_get_ic( hwnd ))) XSetICFocus( xic ); + xim_set_focus( hwnd, TRUE ); + if (use_take_focus) { if (hwnd == NtUserGetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE ); @@ -882,7 +882,6 @@ static void focus_out( Display *display , HWND hwnd ) HWND hwnd_tmp; Window focus_win; int revert; - XIC xic; struct x11drv_win_data *data; if (xim_in_compose_mode()) return; @@ -908,7 +907,7 @@ static void focus_out( Display *display , HWND hwnd ) } x11drv_thread_data()->last_focus = hwnd; - if ((xic = X11DRV_get_ic( hwnd ))) XUnsetICFocus( xic ); + xim_set_focus( hwnd, FALSE ); if (is_virtual_desktop()) { diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 8d3f75a2a2e..5d208bdce39 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -880,6 +880,7 @@ extern void xim_thread_attach( struct x11drv_thread_data *data ) DECLSPEC_HIDDEN extern BOOL xim_in_compose_mode(void) DECLSPEC_HIDDEN; extern void X11DRV_XIMLookupChars( const char *str, UINT count ) DECLSPEC_HIDDEN; extern XIC X11DRV_get_ic( HWND hwnd ) DECLSPEC_HIDDEN; +extern void xim_set_focus( HWND hwnd, BOOL focus ) DECLSPEC_HIDDEN; #define XEMBED_MAPPED (1 << 0) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 0d12b155da4..760bbae42d8 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -471,3 +471,13 @@ XIC X11DRV_get_ic( HWND hwnd ) return ret; } + +void xim_set_focus( HWND hwnd, BOOL focus ) +{ + XIC xic; + + if (!(xic = X11DRV_get_ic( hwnd ))) return; + + if (focus) XSetICFocus( xic ); + else XUnsetICFocus( xic ); +} From ca1549c7f1dbbd160d54b38ca216702226274752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 14:29:18 +0200 Subject: [PATCH 320/758] winex11: Post internal WM_IME_NOTIFY wparam on composition updates. (cherry picked from commit 9b4c09d8c43de61a5cf9e1918cc4d6373aa971f0) --- dlls/imm32/ime.c | 25 +++++++++++++ dlls/winex11.drv/init.c | 1 + dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/x11drv.h | 4 +- dlls/winex11.drv/xim.c | 75 ++++++++++++++++++++++++++++++++++++- include/ntuser.h | 1 + 6 files changed, 104 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 46f12ef6f45..35e94404b99 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -41,6 +41,7 @@ static const char *debugstr_imn( WPARAM wparam ) case IMN_GUIDELINE: return "IMN_GUIDELINE"; case IMN_SETSTATUSWINDOWPOS: return "IMN_SETSTATUSWINDOWPOS"; case IMN_WINE_SET_OPEN_STATUS: return "IMN_WINE_SET_OPEN_STATUS"; + case IMN_WINE_SET_COMP_STRING: return "IMN_WINE_SET_COMP_STRING"; default: return wine_dbg_sprintf( "%#Ix", wparam ); } } @@ -338,6 +339,28 @@ static void ime_ui_start_composition( HIMC himc, HWND hwnd ) ImmUnlockIMC( himc ); } +static UINT ime_set_comp_string( HIMC himc, LPARAM lparam ) +{ + union + { + struct + { + UINT uMsgCount; + TRANSMSG TransMsg[10]; + }; + TRANSMSGLIST list; + } buffer = {.uMsgCount = ARRAY_SIZE(buffer.TransMsg)}; + UINT count; + + TRACE( "himc %p\n", himc ); + + count = ImeToAsciiEx( VK_PROCESSKEY, lparam, NULL, &buffer.list, 0, himc ); + if (count >= buffer.uMsgCount) + WARN( "ImeToAsciiEx returned %#x messages\n", count ); + + return count; +} + static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, WPARAM wparam, LPARAM lparam ) { TRACE( "himc %p, hwnd %p, wparam %s, lparam %#Ix\n", hwnd, himc, debugstr_imn(wparam), lparam ); @@ -346,6 +369,8 @@ static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, WPARAM wparam, LPARAM lparam { case IMN_WINE_SET_OPEN_STATUS: return ImmSetOpenStatus( himc, lparam ); + case IMN_WINE_SET_COMP_STRING: + return ime_set_comp_string( himc, lparam ); default: FIXME( "himc %p, hwnd %p, wparam %s, lparam %#Ix stub!\n", hwnd, himc, debugstr_imn(wparam), lparam ); return 0; diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index bc4627d0b1c..fb879c9b483 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -400,6 +400,7 @@ static const struct user_driver_funcs x11drv_funcs = .pMapVirtualKeyEx = X11DRV_MapVirtualKeyEx, .pToUnicodeEx = X11DRV_ToUnicodeEx, .pVkKeyScanEx = X11DRV_VkKeyScanEx, + .pImeToAsciiEx = X11DRV_ImeToAsciiEx, .pNotifyIMEStatus = X11DRV_NotifyIMEStatus, .pDestroyCursorIcon = X11DRV_DestroyCursorIcon, .pSetCursor = X11DRV_SetCursor, diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 8a0a22c452d..c099c3a2727 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1348,7 +1348,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) if (status == XLookupChars) { - X11DRV_XIMLookupChars( Str, ascii_chars ); + xim_set_result_string( hwnd, Str, ascii_chars ); if (buf != Str) free( Str ); return TRUE; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5d208bdce39..6567eb91b2e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -210,6 +210,8 @@ extern INT X11DRV_GetKeyNameText( LONG lparam, LPWSTR buffer, INT size ) DECLSPE extern UINT X11DRV_MapVirtualKeyEx( UINT code, UINT map_type, HKL hkl ) DECLSPEC_HIDDEN; extern INT X11DRV_ToUnicodeEx( UINT virtKey, UINT scanCode, const BYTE *lpKeyState, LPWSTR bufW, int bufW_size, UINT flags, HKL hkl ) DECLSPEC_HIDDEN; +extern UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT vsc, const BYTE *state, + COMPOSITIONSTRING *compstr, HIMC himc ) DECLSPEC_HIDDEN; extern SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) DECLSPEC_HIDDEN; extern void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status ) DECLSPEC_HIDDEN; extern void X11DRV_DestroyCursorIcon( HCURSOR handle ) DECLSPEC_HIDDEN; @@ -878,7 +880,7 @@ extern struct x11drv_display_device_handler desktop_handler DECLSPEC_HIDDEN; extern BOOL xim_init( const WCHAR *input_style ) DECLSPEC_HIDDEN; extern void xim_thread_attach( struct x11drv_thread_data *data ) DECLSPEC_HIDDEN; extern BOOL xim_in_compose_mode(void) DECLSPEC_HIDDEN; -extern void X11DRV_XIMLookupChars( const char *str, UINT count ) DECLSPEC_HIDDEN; +extern void xim_set_result_string( HWND hwnd, const char *str, UINT count ) DECLSPEC_HIDDEN; extern XIC X11DRV_get_ic( HWND hwnd ) DECLSPEC_HIDDEN; extern void xim_set_focus( HWND hwnd, BOOL focus ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 760bbae42d8..1dde1fc936c 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -27,6 +27,8 @@ #include #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winnls.h" @@ -42,6 +44,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(xim); #define XICProc XIMProc #endif +struct ime_update +{ + struct list entry; + DWORD id; +}; + +static pthread_mutex_t ime_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct list ime_updates = LIST_INIT(ime_updates); +static DWORD ime_update_count; static WCHAR *ime_comp_buf; static XIMStyle input_style = 0; @@ -72,6 +83,21 @@ BOOL xim_in_compose_mode(void) return !!ime_comp_buf; } +static void post_ime_update( HWND hwnd ) +{ + UINT id; + struct ime_update *update; + + if (!(update = malloc( sizeof(struct ime_update) ))) return; + + pthread_mutex_lock( &ime_mutex ); + id = update->id = ++ime_update_count; + list_add_tail( &ime_updates, &update->entry ); + pthread_mutex_unlock( &ime_mutex ); + + NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_COMP_STRING, id ); +} + static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text, UINT new_len ) { UINT len = ime_comp_buf ? wcslen( ime_comp_buf ) : 0; @@ -96,18 +122,20 @@ static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, len * sizeof(WCHAR) ); } -void X11DRV_XIMLookupChars( const char *str, UINT count ) +void xim_set_result_string( HWND hwnd, const char *str, UINT count ) { WCHAR *output; DWORD len; - TRACE("%p %u\n", str, count); + TRACE( "hwnd %p, string %s\n", hwnd, debugstr_an(str, count) ); if (!(output = malloc( (count + 1) * sizeof(WCHAR) ))) return; len = ntdll_umbstowcs( str, count, output, count ); output[len] = 0; + post_ime_update( hwnd ); x11drv_client_func( client_func_ime_set_result, output, len * sizeof(WCHAR) ); + free( output ); } @@ -142,6 +170,8 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0; else ERR( "Failed to allocate preedit buffer\n" ); + post_ime_update( hwnd ); + return -1; } @@ -155,6 +185,8 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) ime_comp_buf = NULL; x11drv_client_call( client_ime_set_composition_status, FALSE ); + post_ime_update( hwnd ); + return 0; } @@ -193,6 +225,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (text && str != text->string.multi_byte) free( str ); x11drv_client_call( client_ime_set_cursor_pos, params->caret ); + post_ime_update( hwnd ); return 0; } @@ -239,6 +272,8 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) x11drv_client_call( client_ime_set_cursor_pos, pos ); params->position = xim_caret_pos = pos; + post_ime_update( hwnd ); + return 0; } @@ -474,10 +509,46 @@ XIC X11DRV_get_ic( HWND hwnd ) void xim_set_focus( HWND hwnd, BOOL focus ) { + struct list updates = LIST_INIT(updates); + struct ime_update *update, *next; XIC xic; if (!(xic = X11DRV_get_ic( hwnd ))) return; if (focus) XSetICFocus( xic ); else XUnsetICFocus( xic ); + + pthread_mutex_lock( &ime_mutex ); + list_move_tail( &updates, &ime_updates ); + pthread_mutex_unlock( &ime_mutex ); + + LIST_FOR_EACH_ENTRY_SAFE( update, next, &updates, struct ime_update, entry ) free( update ); +} + +static struct ime_update *find_ime_update( UINT id ) +{ + struct ime_update *update; + LIST_FOR_EACH_ENTRY( update, &ime_updates, struct ime_update, entry ) + if (update->id == id) return update; + return NULL; +} + +/*********************************************************************** + * ImeToAsciiEx (X11DRV.@) + */ +UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT lparam, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc ) +{ + struct ime_update *update; + + TRACE( "vkey %#x, lparam %#x, state %p, compstr %p, himc %p\n", vkey, lparam, state, compstr, himc ); + + pthread_mutex_lock( &ime_mutex ); + + if ((update = find_ime_update( lparam ))) + list_remove( &update->entry ); + + pthread_mutex_unlock( &ime_mutex ); + + free( update ); + return 0; } diff --git a/include/ntuser.h b/include/ntuser.h index c8a3133a36a..0eb597d2b17 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -494,6 +494,7 @@ enum wine_internal_message /* internal WM_IME_NOTIFY wparams, not compatible with Windows */ #define IMN_WINE_SET_OPEN_STATUS 0x000f +#define IMN_WINE_SET_COMP_STRING 0x0010 /* builtin IME driver calls */ enum wine_ime_call From 2a0c390fd34fb76660a23506aefa7163d16dc3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 13 May 2023 10:53:42 +0200 Subject: [PATCH 321/758] winex11: Include the XIM preedit and result text into the IME updates. (cherry picked from commit c28f571a55e4eb376c91207b5c4b635cdb19b82d) --- dlls/winex11.drv/xim.c | 59 +++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 1dde1fc936c..efe9498c054 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -48,6 +48,10 @@ struct ime_update { struct list entry; DWORD id; + DWORD cursor_pos; + WCHAR *comp_str; + WCHAR *result_str; + WCHAR buffer[]; }; static pthread_mutex_t ime_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -83,12 +87,18 @@ BOOL xim_in_compose_mode(void) return !!ime_comp_buf; } -static void post_ime_update( HWND hwnd ) +static void post_ime_update( HWND hwnd, UINT cursor_pos, WCHAR *comp_str, WCHAR *result_str ) { - UINT id; + UINT id, comp_len, result_len; struct ime_update *update; - if (!(update = malloc( sizeof(struct ime_update) ))) return; + comp_len = comp_str ? wcslen( comp_str ) + 1 : 0; + result_len = result_str ? wcslen( result_str ) + 1 : 0; + + if (!(update = malloc( offsetof(struct ime_update, buffer[comp_len + result_len]) ))) return; + update->cursor_pos = cursor_pos; + update->comp_str = comp_str ? memcpy( update->buffer, comp_str, comp_len * sizeof(WCHAR) ) : NULL; + update->result_str = result_str ? memcpy( update->buffer + comp_len, result_str, result_len * sizeof(WCHAR) ) : NULL; pthread_mutex_lock( &ime_mutex ); id = update->id = ++ime_update_count; @@ -133,7 +143,7 @@ void xim_set_result_string( HWND hwnd, const char *str, UINT count ) len = ntdll_umbstowcs( str, count, output, count ); output[len] = 0; - post_ime_update( hwnd ); + post_ime_update( hwnd, 0, ime_comp_buf, output ); x11drv_client_func( client_func_ime_set_result, output, len * sizeof(WCHAR) ); free( output ); @@ -170,7 +180,7 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0; else ERR( "Failed to allocate preedit buffer\n" ); - post_ime_update( hwnd ); + post_ime_update( hwnd, 0, ime_comp_buf, NULL ); return -1; } @@ -185,7 +195,7 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) ime_comp_buf = NULL; x11drv_client_call( client_ime_set_composition_status, FALSE ); - post_ime_update( hwnd ); + post_ime_update( hwnd, 0, NULL, NULL ); return 0; } @@ -225,7 +235,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (text && str != text->string.multi_byte) free( str ); x11drv_client_call( client_ime_set_cursor_pos, params->caret ); - post_ime_update( hwnd ); + post_ime_update( hwnd, params->caret, ime_comp_buf, NULL ); return 0; } @@ -272,7 +282,7 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) x11drv_client_call( client_ime_set_cursor_pos, pos ); params->position = xim_caret_pos = pos; - post_ime_update( hwnd ); + post_ime_update( hwnd, pos, ime_comp_buf, NULL ); return 0; } @@ -538,15 +548,44 @@ static struct ime_update *find_ime_update( UINT id ) */ UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT lparam, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc ) { + UINT needed = sizeof(COMPOSITIONSTRING), comp_len, result_len; struct ime_update *update; TRACE( "vkey %#x, lparam %#x, state %p, compstr %p, himc %p\n", vkey, lparam, state, compstr, himc ); pthread_mutex_lock( &ime_mutex ); - if ((update = find_ime_update( lparam ))) - list_remove( &update->entry ); + if (!(update = find_ime_update( lparam ))) + { + pthread_mutex_unlock( &ime_mutex ); + return 0; + } + + if (!update->comp_str) comp_len = 0; + else + { + comp_len = wcslen( update->comp_str ); + needed += comp_len * sizeof(WCHAR); /* GCS_COMPSTR */ + needed += comp_len; /* GCS_COMPATTR */ + needed += 2 * sizeof(DWORD); /* GCS_COMPCLAUSE */ + } + + if (!update->result_str) result_len = 0; + else + { + result_len = wcslen( update->result_str ); + needed += result_len * sizeof(WCHAR); /* GCS_RESULTSTR */ + needed += 2 * sizeof(DWORD); /* GCS_RESULTCLAUSE */ + } + + if (compstr->dwSize < needed) + { + compstr->dwSize = needed; + pthread_mutex_unlock( &ime_mutex ); + return STATUS_BUFFER_TOO_SMALL; + } + list_remove( &update->entry ); pthread_mutex_unlock( &ime_mutex ); free( update ); From b0a2c2a70ee07bd85c13fed0cb48010d86300f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 14:29:18 +0200 Subject: [PATCH 322/758] winex11: Generate IME messages using WM_IME_NOTIFY instead of callbacks. (cherry picked from commit b9bfd74acc0992ef56f814b60e831cba01f6be85) --- dlls/imm32/ime.c | 18 ++ dlls/winex11.drv/dllmain.c | 4 - dlls/winex11.drv/ime.c | 472 ---------------------------------- dlls/winex11.drv/unixlib.h | 4 - dlls/winex11.drv/x11drv_dll.h | 4 - dlls/winex11.drv/xim.c | 62 ++++- 6 files changed, 69 insertions(+), 495 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 35e94404b99..a4cdcade73e 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -350,13 +350,31 @@ static UINT ime_set_comp_string( HIMC himc, LPARAM lparam ) }; TRANSMSGLIST list; } buffer = {.uMsgCount = ARRAY_SIZE(buffer.TransMsg)}; + INPUTCONTEXT *ctx; + TRANSMSG *msgs; + HIMCC himcc; UINT count; TRACE( "himc %p\n", himc ); + if (!(ctx = ImmLockIMC( himc ))) return 0; + count = ImeToAsciiEx( VK_PROCESSKEY, lparam, NULL, &buffer.list, 0, himc ); if (count >= buffer.uMsgCount) WARN( "ImeToAsciiEx returned %#x messages\n", count ); + else if (!(himcc = ImmReSizeIMCC( ctx->hMsgBuf, (ctx->dwNumMsgBuf + count) * sizeof(*msgs) ))) + WARN( "Failed to resize input context message buffer\n" ); + else if (!(msgs = ImmLockIMCC( (ctx->hMsgBuf = himcc) ))) + WARN( "Failed to lock input context message buffer\n" ); + else + { + memcpy( msgs + ctx->dwNumMsgBuf, buffer.TransMsg, count * sizeof(*msgs) ); + ImmUnlockIMCC( ctx->hMsgBuf ); + ctx->dwNumMsgBuf += count; + } + + ImmUnlockIMC( himc ); + ImmGenerateMessage( himc ); return count; } diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index a3375e60957..7c463d4e401 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -30,8 +30,6 @@ static const callback_func callback_funcs[] = { x11drv_dnd_drop_event, x11drv_dnd_leave_event, - x11drv_ime_set_composition_status, - x11drv_ime_set_cursor_pos, x11drv_ime_update_association, }; @@ -50,8 +48,6 @@ static const kernel_callback kernel_callbacks[] = x11drv_dnd_enter_event, x11drv_dnd_position_event, x11drv_dnd_post_drop, - x11drv_ime_set_composition_string, - x11drv_ime_set_result, x11drv_systray_change_owner, }; diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 11832996084..16357e80548 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -52,20 +52,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); static HIMC *hSelectedFrom = NULL; static INT hSelectedCount = 0; -static void input_context_reset_comp_str( INPUTCONTEXT *ctx ) -{ - COMPOSITIONSTRING *compstr; - - if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) - WARN( "Failed to lock input context composition string\n" ); - else - { - memset( compstr, 0, sizeof(*compstr) ); - compstr->dwSize = sizeof(*compstr); - ImmUnlockIMCC( ctx->hCompStr ); - } -} - static HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_X11) @@ -100,359 +86,6 @@ static BOOL UnlockRealIMC(HIMC hIMC) return FALSE; } -static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset, - LPBYTE target, LPBYTE source, DWORD* lenParam, - DWORD* offsetParam, BOOL wchars ) -{ - if (origLen > 0 && origOffset > 0) - { - int truelen = origLen; - if (wchars) - truelen *= sizeof(WCHAR); - - memcpy(&target[currentOffset], &source[origOffset], truelen); - - *lenParam = origLen; - *offsetParam = currentOffset; - currentOffset += truelen; - } - return currentOffset; -} - -static HIMCC updateCompStr(HIMCC old, LPCWSTR compstr, DWORD len) -{ - /* We need to make sure the CompStr, CompClause and CompAttr fields are all - * set and correct. */ - int needed_size; - HIMCC rc; - LPBYTE newdata = NULL; - LPBYTE olddata = NULL; - LPCOMPOSITIONSTRING new_one; - LPCOMPOSITIONSTRING lpcs = NULL; - INT current_offset = 0; - - TRACE("%s, %li\n",debugstr_wn(compstr,len),len); - - if (old == NULL && compstr == NULL && len == 0) - return NULL; - - if (compstr == NULL && len != 0) - { - ERR("compstr is NULL however we have a len! Please report\n"); - len = 0; - } - - if (old != NULL) - { - olddata = ImmLockIMCC(old); - lpcs = (LPCOMPOSITIONSTRING)olddata; - } - - needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + - len + sizeof(DWORD) * 2; - - if (lpcs != NULL) - { - needed_size += lpcs->dwCompReadAttrLen; - needed_size += lpcs->dwCompReadClauseLen; - needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwResultReadClauseLen; - needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwResultClauseLen; - needed_size += lpcs->dwResultStrLen * sizeof(WCHAR); - needed_size += lpcs->dwPrivateSize; - } - rc = ImmCreateIMCC(needed_size); - newdata = ImmLockIMCC(rc); - new_one = (LPCOMPOSITIONSTRING)newdata; - - new_one->dwSize = needed_size; - current_offset = sizeof(COMPOSITIONSTRING); - if (lpcs != NULL) - { - current_offset = updateField(lpcs->dwCompReadAttrLen, - lpcs->dwCompReadAttrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadAttrLen, - &new_one->dwCompReadAttrOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadClauseLen, - lpcs->dwCompReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadClauseLen, - &new_one->dwCompReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadStrLen, - lpcs->dwCompReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadStrLen, - &new_one->dwCompReadStrOffset, TRUE); - - /* new CompAttr, CompClause, CompStr, dwCursorPos */ - new_one->dwDeltaStart = 0; - - current_offset = updateField(lpcs->dwResultReadClauseLen, - lpcs->dwResultReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadClauseLen, - &new_one->dwResultReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwResultReadStrLen, - lpcs->dwResultReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadStrLen, - &new_one->dwResultReadStrOffset, TRUE); - - current_offset = updateField(lpcs->dwResultClauseLen, - lpcs->dwResultClauseOffset, - current_offset, newdata, olddata, - &new_one->dwResultClauseLen, - &new_one->dwResultClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwResultStrLen, - lpcs->dwResultStrOffset, - current_offset, newdata, olddata, - &new_one->dwResultStrLen, - &new_one->dwResultStrOffset, TRUE); - - current_offset = updateField(lpcs->dwPrivateSize, - lpcs->dwPrivateOffset, - current_offset, newdata, olddata, - &new_one->dwPrivateSize, - &new_one->dwPrivateOffset, FALSE); - } - - /* set new data */ - /* CompAttr */ - new_one->dwCompAttrLen = len; - if (len > 0) - { - new_one->dwCompAttrOffset = current_offset; - memset(&newdata[current_offset],ATTR_INPUT,len); - current_offset += len; - } - - /* CompClause */ - if (len > 0) - { - new_one->dwCompClauseLen = sizeof(DWORD) * 2; - new_one->dwCompClauseOffset = current_offset; - *(DWORD*)(&newdata[current_offset]) = 0; - current_offset += sizeof(DWORD); - *(DWORD*)(&newdata[current_offset]) = len; - current_offset += sizeof(DWORD); - } - else - new_one->dwCompClauseLen = 0; - - /* CompStr */ - new_one->dwCompStrLen = len; - if (len > 0) - { - new_one->dwCompStrOffset = current_offset; - memcpy(&newdata[current_offset],compstr,len*sizeof(WCHAR)); - } - - /* CursorPos */ - new_one->dwCursorPos = len; - - ImmUnlockIMCC(rc); - if (lpcs) - ImmUnlockIMCC(old); - - return rc; -} - -static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len) -{ - /* we need to make sure the ResultStr and ResultClause fields are all - * set and correct */ - int needed_size; - HIMCC rc; - LPBYTE newdata = NULL; - LPBYTE olddata = NULL; - LPCOMPOSITIONSTRING new_one; - LPCOMPOSITIONSTRING lpcs = NULL; - INT current_offset = 0; - - TRACE("%s, %li\n",debugstr_wn(resultstr,len),len); - - if (old == NULL && resultstr == NULL && len == 0) - return NULL; - - if (resultstr == NULL && len != 0) - { - ERR("resultstr is NULL however we have a len! Please report\n"); - len = 0; - } - - if (old != NULL) - { - olddata = ImmLockIMCC(old); - lpcs = (LPCOMPOSITIONSTRING)olddata; - } - - needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + - sizeof(DWORD) * 2; - - if (lpcs != NULL) - { - needed_size += lpcs->dwCompReadAttrLen; - needed_size += lpcs->dwCompReadClauseLen; - needed_size += lpcs->dwCompReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwCompAttrLen; - needed_size += lpcs->dwCompClauseLen; - needed_size += lpcs->dwCompStrLen * sizeof(WCHAR); - needed_size += lpcs->dwResultReadClauseLen; - needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR); - needed_size += lpcs->dwPrivateSize; - } - rc = ImmCreateIMCC(needed_size); - newdata = ImmLockIMCC(rc); - new_one = (LPCOMPOSITIONSTRING)newdata; - - new_one->dwSize = needed_size; - current_offset = sizeof(COMPOSITIONSTRING); - if (lpcs != NULL) - { - current_offset = updateField(lpcs->dwCompReadAttrLen, - lpcs->dwCompReadAttrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadAttrLen, - &new_one->dwCompReadAttrOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadClauseLen, - lpcs->dwCompReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadClauseLen, - &new_one->dwCompReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwCompReadStrLen, - lpcs->dwCompReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwCompReadStrLen, - &new_one->dwCompReadStrOffset, TRUE); - - current_offset = updateField(lpcs->dwCompAttrLen, - lpcs->dwCompAttrOffset, - current_offset, newdata, olddata, - &new_one->dwCompAttrLen, - &new_one->dwCompAttrOffset, FALSE); - - current_offset = updateField(lpcs->dwCompClauseLen, - lpcs->dwCompClauseOffset, - current_offset, newdata, olddata, - &new_one->dwCompClauseLen, - &new_one->dwCompClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwCompStrLen, - lpcs->dwCompStrOffset, - current_offset, newdata, olddata, - &new_one->dwCompStrLen, - &new_one->dwCompStrOffset, TRUE); - - new_one->dwCursorPos = lpcs->dwCursorPos; - new_one->dwDeltaStart = 0; - - current_offset = updateField(lpcs->dwResultReadClauseLen, - lpcs->dwResultReadClauseOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadClauseLen, - &new_one->dwResultReadClauseOffset, FALSE); - - current_offset = updateField(lpcs->dwResultReadStrLen, - lpcs->dwResultReadStrOffset, - current_offset, newdata, olddata, - &new_one->dwResultReadStrLen, - &new_one->dwResultReadStrOffset, TRUE); - - /* new ResultClause , ResultStr */ - - current_offset = updateField(lpcs->dwPrivateSize, - lpcs->dwPrivateOffset, - current_offset, newdata, olddata, - &new_one->dwPrivateSize, - &new_one->dwPrivateOffset, FALSE); - } - - /* set new data */ - /* ResultClause */ - if (len > 0) - { - new_one->dwResultClauseLen = sizeof(DWORD) * 2; - new_one->dwResultClauseOffset = current_offset; - *(DWORD*)(&newdata[current_offset]) = 0; - current_offset += sizeof(DWORD); - *(DWORD*)(&newdata[current_offset]) = len; - current_offset += sizeof(DWORD); - } - else - new_one->dwResultClauseLen = 0; - - /* ResultStr */ - new_one->dwResultStrLen = len; - if (len > 0) - { - new_one->dwResultStrOffset = current_offset; - memcpy(&newdata[current_offset],resultstr,len*sizeof(WCHAR)); - } - ImmUnlockIMCC(rc); - if (lpcs) - ImmUnlockIMCC(old); - - return rc; -} - -static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, - LPARAM lParam) -{ - LPINPUTCONTEXT lpIMC; - LPTRANSMSG lpTransMsg; - - lpIMC = LockRealIMC(hIMC); - if (lpIMC == NULL) - return; - - lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * - sizeof(TRANSMSG)); - if (!lpIMC->hMsgBuf) - return; - - lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); - if (!lpTransMsg) - return; - - lpTransMsg += lpIMC->dwNumMsgBuf; - lpTransMsg->message = msg; - lpTransMsg->wParam = wParam; - lpTransMsg->lParam = lParam; - - ImmUnlockIMCC(lpIMC->hMsgBuf); - lpIMC->dwNumMsgBuf++; - - ImmGenerateMessage(RealIMC(hIMC)); - UnlockRealIMC(hIMC); -} - -static void ime_set_composition_status( HIMC himc, BOOL composition ) -{ - struct ime_private *priv; - INPUTCONTEXT *ctx; - UINT msg = 0; - - if (!(ctx = ImmLockIMC( himc ))) return; - if ((priv = ImmLockIMCC( ctx->hPrivate ))) - { - if (!priv->bInComposition && composition) msg = WM_IME_STARTCOMPOSITION; - else if (priv->bInComposition && !composition) msg = WM_IME_ENDCOMPOSITION; - priv->bInComposition = composition; - ImmUnlockIMCC( ctx->hPrivate ); - } - ImmUnlockIMC( himc ); - - if (msg) GenerateIMEMessage( himc, msg, 0, 0 ); -} - static BOOL IME_RemoveFromSelected(HIMC hIMC) { int i; @@ -516,57 +149,6 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) /* Interfaces to XIM and other parts of winex11drv */ -NTSTATUS x11drv_ime_set_composition_status( UINT open ) -{ - HIMC imc; - LPINPUTCONTEXT lpIMC; - - imc = RealIMC(FROM_X11); - lpIMC = ImmLockIMC(imc); - if (lpIMC == NULL) - return 0; - - if (!open) - { - struct ime_private *myPrivate = ImmLockIMCC(lpIMC->hPrivate); - ShowWindow(myPrivate->hwndDefault, SW_HIDE); - input_context_reset_comp_str( lpIMC ); - ImmUnlockIMCC(lpIMC->hPrivate); - } - - ImmUnlockIMC(imc); - - ime_set_composition_status( imc, open ); - - return 0; -} - -NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) -{ - LPINPUTCONTEXT lpIMC; - LPCOMPOSITIONSTRING compstr; - - if (!hSelectedFrom) - return 0; - - lpIMC = LockRealIMC(FROM_X11); - if (!lpIMC) - return 0; - - compstr = ImmLockIMCC(lpIMC->hCompStr); - if (!compstr) - { - UnlockRealIMC(FROM_X11); - return 0; - } - - compstr->dwCursorPos = pos; - ImmUnlockIMCC(lpIMC->hCompStr); - UnlockRealIMC(FROM_X11); - GenerateIMEMessage(FROM_X11, WM_IME_COMPOSITION, pos, GCS_CURSORPOS); - return 0; -} - NTSTATUS x11drv_ime_update_association( UINT arg ) { HWND focus = UlongToHandle( arg ); @@ -577,57 +159,3 @@ NTSTATUS x11drv_ime_update_association( UINT arg ) ImmAssociateContext(focus,RealIMC(FROM_X11)); return 0; } - - -NTSTATUS WINAPI x11drv_ime_set_composition_string( void *param, ULONG size ) -{ - return ImmSetCompositionStringW( RealIMC(FROM_X11), SCS_SETSTR, param, size, NULL, 0 ); -} - -NTSTATUS WINAPI x11drv_ime_set_result( void *params, ULONG len ) -{ - WCHAR *lpResult = params; - HIMC imc; - LPINPUTCONTEXT lpIMC; - HIMCC newCompStr; - LPIMEPRIVATE myPrivate; - BOOL inComp; - HWND focus; - - len /= sizeof(WCHAR); - if ((focus = GetFocus())) - x11drv_ime_update_association( HandleToUlong( focus )); - - imc = RealIMC(FROM_X11); - lpIMC = ImmLockIMC(imc); - if (lpIMC == NULL) - return 0; - - newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - newCompStr = updateResultStr(lpIMC->hCompStr, lpResult, len); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = newCompStr; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - inComp = myPrivate->bInComposition; - ImmUnlockIMCC(lpIMC->hPrivate); - - if (!inComp) - { - ImmSetOpenStatus(imc, TRUE); - GenerateIMEMessage(imc, WM_IME_STARTCOMPOSITION, 0, 0); - } - - GenerateIMEMessage(imc, WM_IME_COMPOSITION, 0, GCS_COMPSTR); - GenerateIMEMessage(imc, WM_IME_COMPOSITION, lpResult[0], GCS_RESULTSTR|GCS_RESULTCLAUSE); - GenerateIMEMessage(imc, WM_IME_ENDCOMPOSITION, 0, 0); - - if (!inComp) - ImmSetOpenStatus(imc, FALSE); - - ImmUnlockIMC(imc); - return 0; -} diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index ef965381479..dba2c8e1060 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -81,8 +81,6 @@ enum x11drv_client_funcs client_func_dnd_enter_event, client_func_dnd_position_event, client_func_dnd_post_drop, - client_func_ime_set_composition_string, - client_func_ime_set_result, client_func_systray_change_owner, client_func_last }; @@ -94,8 +92,6 @@ enum client_callback { client_dnd_drop_event, client_dnd_leave_event, - client_ime_set_composition_status, - client_ime_set_cursor_pos, client_ime_update_association, client_funcs_count }; diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 2cc27abd362..fbd2a3e74b2 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -30,14 +30,10 @@ extern NTSTATUS WINAPI x11drv_dnd_enter_event( void *params, ULONG size ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI x11drv_dnd_position_event( void *params, ULONG size ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI x11drv_dnd_post_drop( void *data, ULONG size ) DECLSPEC_HIDDEN; -extern NTSTATUS WINAPI x11drv_ime_set_composition_string( void *params, ULONG size ) DECLSPEC_HIDDEN; -extern NTSTATUS WINAPI x11drv_ime_set_result( void *params, ULONG size ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI x11drv_systray_change_owner( void *params, ULONG size ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN; extern LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index efe9498c054..e5cea5941f7 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -126,10 +126,7 @@ static void xim_update_comp_string( UINT offset, UINT old_len, const WCHAR *text ptr = ime_comp_buf + offset; memmove( ptr + new_len, ptr + old_len, (len - offset - old_len) * sizeof(WCHAR) ); if (text) memcpy( ptr, text, new_len * sizeof(WCHAR) ); - len += diff; - ime_comp_buf[len] = 0; - - x11drv_client_func( client_func_ime_set_composition_string, ime_comp_buf, len * sizeof(WCHAR) ); + ime_comp_buf[len + diff] = 0; } void xim_set_result_string( HWND hwnd, const char *str, UINT count ) @@ -144,7 +141,6 @@ void xim_set_result_string( HWND hwnd, const char *str, UINT count ) output[len] = 0; post_ime_update( hwnd, 0, ime_comp_buf, output ); - x11drv_client_func( client_func_ime_set_result, output, len * sizeof(WCHAR) ); free( output ); } @@ -160,10 +156,10 @@ static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg ) switch (state) { case XIMPreeditEnable: - send_message( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE ); + NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE ); break; case XIMPreeditDisable: - send_message( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE ); + NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE ); break; } @@ -176,7 +172,6 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg ); - x11drv_client_call( client_ime_set_composition_status, TRUE ); if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0; else ERR( "Failed to allocate preedit buffer\n" ); @@ -194,7 +189,6 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) free( ime_comp_buf ); ime_comp_buf = NULL; - x11drv_client_call( client_ime_set_composition_status, FALSE ); post_ime_update( hwnd, 0, NULL, NULL ); return 0; @@ -234,7 +228,6 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (text && str != text->string.multi_byte) free( str ); - x11drv_client_call( client_ime_set_cursor_pos, params->caret ); post_ime_update( hwnd, params->caret, ime_comp_buf, NULL ); return 0; @@ -279,7 +272,6 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) FIXME( "Not implemented\n" ); break; } - x11drv_client_call( client_ime_set_cursor_pos, pos ); params->position = xim_caret_pos = pos; post_ime_update( hwnd, pos, ime_comp_buf, NULL ); @@ -545,11 +537,16 @@ static struct ime_update *find_ime_update( UINT id ) /*********************************************************************** * ImeToAsciiEx (X11DRV.@) + * + * As XIM filters key events upfront, we don't use ImeProcessKey and ImeToAsciiEx is instead called + * back from the IME UI window procedure when WM_IME_NOTIFY / IMN_WINE_SET_COMP_STRING messages are + * sent to it, to retrieve composition string updates and generate WM_IME messages. */ UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT lparam, const BYTE *state, COMPOSITIONSTRING *compstr, HIMC himc ) { UINT needed = sizeof(COMPOSITIONSTRING), comp_len, result_len; struct ime_update *update; + void *dst; TRACE( "vkey %#x, lparam %#x, state %p, compstr %p, himc %p\n", vkey, lparam, state, compstr, himc ); @@ -588,6 +585,49 @@ UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT lparam, const BYTE *state, COMPOSITION list_remove( &update->entry ); pthread_mutex_unlock( &ime_mutex ); + memset( compstr, 0, sizeof(*compstr) ); + compstr->dwSize = sizeof(*compstr); + + if (update->comp_str) + { + compstr->dwCursorPos = update->cursor_pos; + + compstr->dwCompStrLen = comp_len; + compstr->dwCompStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompStrOffset; + memcpy( dst, update->comp_str, compstr->dwCompStrLen * sizeof(WCHAR) ); + compstr->dwSize += compstr->dwCompStrLen * sizeof(WCHAR); + + compstr->dwCompClauseLen = 2 * sizeof(DWORD); + compstr->dwCompClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwCompStrLen; + compstr->dwSize += compstr->dwCompClauseLen; + + compstr->dwCompAttrLen = compstr->dwCompStrLen; + compstr->dwCompAttrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwCompAttrOffset; + memset( dst, ATTR_INPUT, compstr->dwCompAttrLen ); + compstr->dwSize += compstr->dwCompAttrLen; + } + + if (update->result_str) + { + compstr->dwResultStrLen = result_len; + compstr->dwResultStrOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwResultStrOffset; + memcpy( dst, update->result_str, compstr->dwResultStrLen * sizeof(WCHAR) ); + compstr->dwSize += compstr->dwResultStrLen * sizeof(WCHAR); + + compstr->dwResultClauseLen = 2 * sizeof(DWORD); + compstr->dwResultClauseOffset = compstr->dwSize; + dst = (BYTE *)compstr + compstr->dwResultClauseOffset; + *((DWORD *)dst + 0) = 0; + *((DWORD *)dst + 1) = compstr->dwResultStrLen; + compstr->dwSize += compstr->dwResultClauseLen; + } + free( update ); return 0; } From 8f9827f0f0fc06cf75cde51ae7c8a09df9979e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 13 May 2023 10:59:32 +0200 Subject: [PATCH 323/758] winex11: Drop the x11drv_ime_update_association user callback. (cherry picked from commit 9f05a79bd63795664bcd20f69a81e1437536dad2) --- dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 13 ------------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 6 ++---- 5 files changed, 2 insertions(+), 20 deletions(-) diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 7c463d4e401..3aa0a9875d0 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -30,7 +30,6 @@ static const callback_func callback_funcs[] = { x11drv_dnd_drop_event, x11drv_dnd_leave_event, - x11drv_ime_update_association, }; C_ASSERT( ARRAYSIZE(callback_funcs) == client_funcs_count ); diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 16357e80548..019312cfd5a 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -146,16 +146,3 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) return TRUE; } - -/* Interfaces to XIM and other parts of winex11drv */ - -NTSTATUS x11drv_ime_update_association( UINT arg ) -{ - HWND focus = UlongToHandle( arg ); - - ImmGetContext(focus); - - if (focus && hSelectedFrom) - ImmAssociateContext(focus,RealIMC(FROM_X11)); - return 0; -} diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index dba2c8e1060..0c812896fbc 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -92,7 +92,6 @@ enum client_callback { client_dnd_drop_event, client_dnd_leave_event, - client_ime_update_association, client_funcs_count }; diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index fbd2a3e74b2..bab27afce14 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -34,7 +34,6 @@ extern NTSTATUS WINAPI x11drv_systray_change_owner( void *params, ULONG size ) D extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN; extern LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index e5cea5941f7..1c3d2dd9875 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -410,8 +410,6 @@ static void xim_open( Display *display, XPointer user, XPointer arg ) TRACE( "display %p, data %p, arg %p\n", display, user, arg ); if (!(data->xim = xim_create( data ))) return; XUnregisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, user ); - - x11drv_client_call( client_ime_update_association, 0 ); } static void xim_destroy( XIM xim, XPointer user, XPointer arg ) @@ -434,8 +432,8 @@ void xim_thread_attach( struct x11drv_thread_data *data ) for (i = 0; list && i < count; ++i) TRACE( " %d: %s\n", i, list[i] ); if (list) XFreeStringList( list ); - if ((data->xim = xim_create( data ))) x11drv_client_call( client_ime_update_association, 0 ); - else XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, (XPointer)data ); + if ((data->xim = xim_create( data ))) return; + XRegisterIMInstantiateCallback( display, NULL, NULL, NULL, xim_open, (XPointer)data ); } static BOOL xic_destroy( XIC xic, XPointer user, XPointer arg ) From bf5ea29bbf267e6d40da5b74a4a416b959526ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 22:24:19 +0200 Subject: [PATCH 324/758] winex11: Remove now unnecessary selected HIMC tracking code. (cherry picked from commit cbf719d72d885e9da96acf1fec6fc89ca95660e7) --- dlls/winex11.drv/ime.c | 82 ++---------------------------------------- 1 file changed, 3 insertions(+), 79 deletions(-) diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 019312cfd5a..daf6d89dc7a 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -47,91 +47,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); -#define FROM_X11 ((HIMC)0xcafe1337) - -static HIMC *hSelectedFrom = NULL; -static INT hSelectedCount = 0; - -static HIMC RealIMC(HIMC hIMC) -{ - if (hIMC == FROM_X11) - { - INT i; - HWND wnd = GetFocus(); - HIMC winHimc = ImmGetContext(wnd); - for (i = 0; i < hSelectedCount; i++) - if (winHimc == hSelectedFrom[i]) - return winHimc; - return NULL; - } - else - return hIMC; -} - -static LPINPUTCONTEXT LockRealIMC(HIMC hIMC) -{ - HIMC real_imc = RealIMC(hIMC); - if (real_imc) - return ImmLockIMC(real_imc); - else - return NULL; -} - -static BOOL UnlockRealIMC(HIMC hIMC) -{ - HIMC real_imc = RealIMC(hIMC); - if (real_imc) - return ImmUnlockIMC(real_imc); - else - return FALSE; -} - -static BOOL IME_RemoveFromSelected(HIMC hIMC) -{ - int i; - for (i = 0; i < hSelectedCount; i++) - if (hSelectedFrom[i] == hIMC) - { - if (i < hSelectedCount - 1) - memmove(&hSelectedFrom[i], &hSelectedFrom[i+1], (hSelectedCount - i - 1)*sizeof(HIMC)); - hSelectedCount --; - return TRUE; - } - return FALSE; -} - -static void IME_AddToSelected(HIMC hIMC) -{ - hSelectedCount++; - if (hSelectedFrom) - hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount*sizeof(HIMC)); - else - hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC)); - hSelectedFrom[hSelectedCount-1] = hIMC; -} - BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) { LPINPUTCONTEXT lpIMC; TRACE("%p %s\n",hIMC,(fSelect)?"TRUE":"FALSE"); - if (hIMC == FROM_X11) - { - ERR("ImeSelect should never be called from X11\n"); - return FALSE; - } - - if (!hIMC) - return TRUE; - - /* not selected */ - if (!fSelect) - return IME_RemoveFromSelected(hIMC); - - IME_AddToSelected(hIMC); + if (!hIMC || !fSelect) return TRUE; /* Initialize our structures */ - lpIMC = LockRealIMC(hIMC); + lpIMC = ImmLockIMC(hIMC); if (lpIMC != NULL) { LPIMEPRIVATE myPrivate; @@ -141,7 +65,7 @@ BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) myPrivate->textfont = NULL; myPrivate->hwndDefault = NULL; ImmUnlockIMCC(lpIMC->hPrivate); - UnlockRealIMC(hIMC); + ImmUnlockIMC(hIMC); } return TRUE; From 017035793d187f243c57db420dd2b16d1ca4142f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 1 Apr 2023 22:25:01 +0200 Subject: [PATCH 325/758] winex11: Use the default IME implementation for ImeSelect. (cherry picked from commit f7d4eec45920ac18cbd55f85918d9a73b6bd5d0c) --- dlls/winex11.drv/Makefile.in | 1 - dlls/winex11.drv/ime.c | 72 ------------------------------- dlls/winex11.drv/winex11.drv.spec | 3 -- 3 files changed, 76 deletions(-) delete mode 100644 dlls/winex11.drv/ime.c diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in index c9e76002b2c..3629ca17c67 100644 --- a/dlls/winex11.drv/Makefile.in +++ b/dlls/winex11.drv/Makefile.in @@ -15,7 +15,6 @@ C_SRCS = \ event.c \ fs.c \ graphics.c \ - ime.c \ init.c \ keyboard.c \ mouse.c \ diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c deleted file mode 100644 index daf6d89dc7a..00000000000 --- a/dlls/winex11.drv/ime.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * The IME for interfacing with XIM - * - * Copyright 2008 CodeWeavers, Aric Stewart - * - * 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 - */ - -/* - * Notes: - * The normal flow for IMM/IME Processing is as follows. - * 1) The Keyboard Driver generates key messages which are first passed to - * the IMM and then to IME via ImeProcessKey. If the IME returns 0 then - * it does not want the key and the keyboard driver then generates the - * WM_KEYUP/WM_KEYDOWN messages. However if the IME is going to process the - * key it returns non-zero. - * 2) If the IME is going to process the key then the IMM calls ImeToAsciiEx to - * process the key. the IME modifies the HIMC structure to reflect the - * current state and generates any messages it needs the IMM to process. - * 3) IMM checks the messages and send them to the application in question. From - * here the IMM level deals with if the application is IME aware or not. - * - * This flow does not work well for the X11 driver and XIM. - * (It works fine for Mac) - * As such we will have to reroute step 1. Instead the x11drv driver will - * generate an XIM events and call directly into this IME implementation. - * As such we will have to use the alternative ImmGenerateMessage path to be - * generate the messages that we want the IMM layer to send to the application. - */ - -#include "x11drv_dll.h" -#include "wine/debug.h" -#include "imm.h" -#include "immdev.h" - -WINE_DEFAULT_DEBUG_CHANNEL(imm); - -BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) -{ - LPINPUTCONTEXT lpIMC; - TRACE("%p %s\n",hIMC,(fSelect)?"TRUE":"FALSE"); - - if (!hIMC || !fSelect) return TRUE; - - /* Initialize our structures */ - lpIMC = ImmLockIMC(hIMC); - if (lpIMC != NULL) - { - LPIMEPRIVATE myPrivate; - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - myPrivate->bInComposition = FALSE; - myPrivate->bInternalState = FALSE; - myPrivate->textfont = NULL; - myPrivate->hwndDefault = NULL; - ImmUnlockIMCC(lpIMC->hPrivate); - ImmUnlockIMC(hIMC); - } - - return TRUE; -} diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index c8d52d07f6d..a7334eef3d9 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -9,6 +9,3 @@ # System tray @ cdecl wine_notify_icon(long ptr) - -#IME Interface -@ stdcall ImeSelect(long long) From 2dbbade11083c650659abe468ce8fee8d39a1e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 10 May 2023 23:34:37 +0200 Subject: [PATCH 326/758] imm32: Get rid of the graphics driver loading mechanism. (cherry picked from commit d61a786461f1b1bfdd6f17125db124cd5e06fa3f) --- dlls/imm32/imm.c | 100 +++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 2fe355da5bb..aa1881e0918 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -429,31 +429,6 @@ static struct imc *query_imc_data( HIMC handle ) return ret && ret->handle == handle ? ret : NULL; } -static HMODULE load_graphics_driver(void) -{ - static const WCHAR key_pathW[] = L"System\\CurrentControlSet\\Control\\Video\\{"; - static const WCHAR displayW[] = L"}\\0000"; - - HMODULE ret = 0; - HKEY hkey; - DWORD size; - WCHAR path[MAX_PATH]; - WCHAR key[ARRAY_SIZE( key_pathW ) + ARRAY_SIZE( displayW ) + 40]; - UINT guid_atom = HandleToULong( GetPropW( GetDesktopWindow(), L"__wine_display_device_guid" )); - - if (!guid_atom) return 0; - memcpy( key, key_pathW, sizeof(key_pathW) ); - if (!GlobalGetAtomNameW( guid_atom, key + lstrlenW(key), 40 )) return 0; - lstrcatW( key, displayW ); - if (RegOpenKeyW( HKEY_LOCAL_MACHINE, key, &hkey )) return 0; - size = sizeof(path); - if (!RegQueryValueExW( hkey, L"GraphicsDriver", NULL, NULL, (BYTE *)path, &size )) - ret = LoadLibraryW( path ); - RegCloseKey( hkey ); - TRACE( "%s %p\n", debugstr_w(path), ret ); - return ret; -} - /* lookup an IME from a HKL, must hold ime_cs */ static struct ime *find_ime_from_hkl( HKL hkl ) { @@ -511,41 +486,54 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) if (use_default_ime) { if (*buffer) WARN( "Failed to load %s, falling back to default.\n", debugstr_w(buffer) ); - if (!(ime->module = load_graphics_driver())) ime->module = LoadLibraryW( L"imm32" ); + ime->module = LoadLibraryW( L"imm32" ); + ime->pImeInquire = (void *)ImeInquire; + ime->pImeDestroy = ImeDestroy; + ime->pImeSelect = ImeSelect; + ime->pImeConfigure = ImeConfigure; + ime->pImeEscape = ImeEscape; + ime->pImeSetActiveContext = ImeSetActiveContext; + ime->pImeToAsciiEx = (void *)ImeToAsciiEx; + ime->pNotifyIME = NotifyIME; + ime->pImeRegisterWord = (void *)ImeRegisterWord; + ime->pImeUnregisterWord = (void *)ImeUnregisterWord; + ime->pImeEnumRegisterWord = (void *)ImeEnumRegisterWord; + ime->pImeSetCompositionString = ImeSetCompositionString; + ime->pImeConversionList = (void *)ImeConversionList; + ime->pImeProcessKey = (void *)ImeProcessKey; + ime->pImeGetRegisterWordStyle = (void *)ImeGetRegisterWordStyle; + ime->pImeGetImeMenuItems = (void *)ImeGetImeMenuItems; } - + else + { #define LOAD_FUNCPTR( f ) \ - if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f )) && \ - !(ime->p##f = use_default_ime ? (void *)f : NULL)) \ - { \ - LeaveCriticalSection( &ime_cs ); \ - WARN( "Can't find function %s in HKL %p IME\n", #f, hkl ); \ - goto failed; \ - } - LOAD_FUNCPTR( ImeInquire ); - LOAD_FUNCPTR( ImeDestroy ); - LOAD_FUNCPTR( ImeSelect ); - LOAD_FUNCPTR( ImeConfigure ); - LOAD_FUNCPTR( ImeEscape ); - LOAD_FUNCPTR( ImeSetActiveContext ); - LOAD_FUNCPTR( ImeToAsciiEx ); - LOAD_FUNCPTR( NotifyIME ); - LOAD_FUNCPTR( ImeRegisterWord ); - LOAD_FUNCPTR( ImeUnregisterWord ); - LOAD_FUNCPTR( ImeEnumRegisterWord ); - LOAD_FUNCPTR( ImeSetCompositionString ); - LOAD_FUNCPTR( ImeConversionList ); - LOAD_FUNCPTR( ImeProcessKey ); - LOAD_FUNCPTR( ImeGetRegisterWordStyle ); - LOAD_FUNCPTR( ImeGetImeMenuItems ); + if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f ))) \ + { \ + WARN( "Can't find function %s in HKL %p IME\n", #f, hkl ); \ + goto failed; \ + } + + LOAD_FUNCPTR( ImeInquire ); + LOAD_FUNCPTR( ImeDestroy ); + LOAD_FUNCPTR( ImeSelect ); + LOAD_FUNCPTR( ImeConfigure ); + LOAD_FUNCPTR( ImeEscape ); + LOAD_FUNCPTR( ImeSetActiveContext ); + LOAD_FUNCPTR( ImeToAsciiEx ); + LOAD_FUNCPTR( NotifyIME ); + LOAD_FUNCPTR( ImeRegisterWord ); + LOAD_FUNCPTR( ImeUnregisterWord ); + LOAD_FUNCPTR( ImeEnumRegisterWord ); + LOAD_FUNCPTR( ImeSetCompositionString ); + LOAD_FUNCPTR( ImeConversionList ); + LOAD_FUNCPTR( ImeProcessKey ); + LOAD_FUNCPTR( ImeGetRegisterWordStyle ); + LOAD_FUNCPTR( ImeGetImeMenuItems ); #undef LOAD_FUNCPTR + } ime->hkl = hkl; - if (!ime->pImeInquire( &ime->info, buffer, 0 )) - { - LeaveCriticalSection( &ime_cs ); - goto failed; - } + if (!ime->pImeInquire( &ime->info, buffer, 0 )) goto failed; if (ime_is_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) ); else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->ui_class, ARRAY_SIZE(ime->ui_class) ); @@ -558,6 +546,8 @@ BOOL WINAPI ImmLoadIME( HKL hkl ) return TRUE; failed: + LeaveCriticalSection( &ime_cs ); + if (ime->module) FreeLibrary( ime->module ); free( ime ); return FALSE; From 066783fced61e84b8ef0278d0f659817a4b66137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 May 2023 21:58:24 +0200 Subject: [PATCH 327/758] imm32: Remove now unused members from ime_private. And move its definition to ime.c. (cherry picked from commit 65d0f7a75626983bb7cc2b6c7ace41d44d730560) --- dlls/imm32/ime.c | 34 +++++++++++++--------------------- include/ntuser.h | 9 --------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index a4cdcade73e..10fd234a2c1 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -23,6 +23,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); +struct ime_private +{ + BOOL in_composition; + HFONT hfont; +}; + static const char *debugstr_imn( WPARAM wparam ) { switch (wparam) @@ -139,7 +145,7 @@ static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) struct ime_private *priv; HFONT font = NULL; if (!(priv = ImmLockIMCC( ctx->hPrivate ))) return NULL; - if (priv->textfont) font = SelectObject( hdc, priv->textfont ); + if (priv->hfont) font = SelectObject( hdc, priv->hfont ); ImmUnlockIMCC( ctx->hPrivate ); return font; } @@ -175,9 +181,9 @@ static UINT ime_set_composition_status( HIMC himc, BOOL composition ) if (!(ctx = ImmLockIMC( himc ))) return 0; if ((priv = ImmLockIMCC( ctx->hPrivate ))) { - if (!priv->bInComposition && composition) msg = WM_IME_STARTCOMPOSITION; - else if (priv->bInComposition && !composition) msg = WM_IME_ENDCOMPOSITION; - priv->bInComposition = composition; + if (!priv->in_composition && composition) msg = WM_IME_STARTCOMPOSITION; + else if (priv->in_composition && !composition) msg = WM_IME_ENDCOMPOSITION; + priv->in_composition = composition; ImmUnlockIMCC( ctx->hPrivate ); } ImmUnlockIMC( himc ); @@ -398,7 +404,6 @@ static LRESULT ime_ui_notify( HIMC himc, HWND hwnd, WPARAM wparam, LPARAM lparam static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); - INPUTCONTEXT *ctx; TRACE( "hwnd %p, himc %p, msg %s, wparam %#Ix, lparam %#Ix\n", hwnd, himc, debugstr_wm_ime(msg), wparam, lparam ); @@ -406,20 +411,7 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP switch (msg) { case WM_CREATE: - { - struct ime_private *priv; - - SetWindowTextA( hwnd, "Wine Ime Active" ); - - if (!(ctx = ImmLockIMC( himc ))) return TRUE; - if ((priv = ImmLockIMCC( ctx->hPrivate ))) - { - priv->hwndDefault = hwnd; - ImmUnlockIMCC( ctx->hPrivate ); - } - ImmUnlockIMC( himc ); return TRUE; - } case WM_PAINT: ime_ui_paint( himc, hwnd ); return FALSE; @@ -468,7 +460,7 @@ BOOL WINAPI ImeInquire( IMEINFO *info, WCHAR *ui_class, DWORD flags ) wcscpy( ui_class, ime_ui_class.lpszClassName ); memset( info, 0, sizeof(*info) ); - info->dwPrivateDataSize = sizeof(IMEPRIVATE); + info->dwPrivateDataSize = sizeof(struct ime_private); info->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; info->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE; info->fdwSentenceCaps = IME_SMODE_AUTOMATIC; @@ -656,8 +648,8 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) case IMC_SETCOMPOSITIONFONT: if ((priv = ImmLockIMCC( ctx->hPrivate ))) { - if (priv->textfont) DeleteObject( priv->textfont ); - priv->textfont = CreateFontIndirectW( &ctx->lfFont.W ); + if (priv->hfont) DeleteObject( priv->hfont ); + priv->hfont = CreateFontIndirectW( &ctx->lfFont.W ); ImmUnlockIMCC( ctx->hPrivate ); } break; diff --git a/include/ntuser.h b/include/ntuser.h index 0eb597d2b17..f9c63a48cee 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -511,15 +511,6 @@ struct ime_driver_call_params COMPOSITIONSTRING *compstr; }; -/* internal IME private */ -typedef struct ime_private -{ - BOOL bInComposition; - BOOL bInternalState; - HFONT textfont; - HWND hwndDefault; -} IMEPRIVATE, *LPIMEPRIVATE; - #define WM_SYSTIMER 0x0118 /* the various structures that can be sent in messages, in platform-independent layout */ From afaac97eb2b1379374fd9d5f675e4e5f437a5db4 Mon Sep 17 00:00:00 2001 From: Jinoh Kang Date: Sat, 20 May 2023 23:34:23 +0900 Subject: [PATCH 328/758] imm32: Don't erroneously start composition when handling IMC_SETOPENSTATUS. Fixes: 8ae0c308233e61cfc01ef52886077960d6513d93 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54965 --- dlls/imm32/ime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 10fd234a2c1..e15367f5fa8 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -657,7 +657,7 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) if (!ctx->fOpen) { input_context_set_comp_str( ctx, NULL, 0 ); - if ((msg = ime_set_composition_status( himc, TRUE ))) ime_send_message( himc, msg, 0, 0 ); + if ((msg = ime_set_composition_status( himc, FALSE ))) ime_send_message( himc, msg, 0, 0 ); } NtUserNotifyIMEStatus( ctx->hWnd, ctx->fOpen ); break; From a8547b46b3a7664623ed8111aad64e641fbffe5a Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 7 Dec 2020 09:31:52 +0100 Subject: [PATCH 329/758] winex11.drv: Recognize the keyboard in a locale-independent way. Try to recognize the keyboard comparing keysyms instead of converting them to multibyte strings, which makes the process locale-dependent and therefore more fragile. Unfortunately this means that the layout tables might need to be updated. However, this change is known to fix the recognitions of a few keys in the French layout. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30984 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45605 CW-Bug-Id: #16793 --- dlls/winex11.drv/keyboard.c | 62 +++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index c099c3a2727..9217ebb46ac 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1403,6 +1403,35 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) return TRUE; } +/* From the point of view of this function there are two types of + * keys: those for which the mapping to vkey and scancode depends on + * the keyboard layout (i.e., letters, numbers, punctuation) and those + * for which it doesn't (control keys); since this function is used to + * recognize the keyboard layout and map keysyms to vkeys and + * scancodes, we are only concerned about the first type, and map + * everything in the second type to zero. + */ +static char keysym_to_char( KeySym keysym ) +{ + /* Dead keys */ + if (0xfe50 <= keysym && keysym < 0xfed0) + return KEYBOARD_MapDeadKeysym( keysym ); + + /* Control keys (there is nothing allocated below 0xfc00, but I + take some margin in case something is added in the future) */ + if (0xf000 <= keysym && keysym < 0x10000) + return 0; + + /* XFree86 vendor keys */ + if (0x10000000 <= keysym) + return 0; + + /* "Normal" keys: return last octet, because our tables don't have + more than that; it would be better to extend the tables and + compare the whole keysym, but it's a lot of work... */ + return keysym & 0xff; +} + /********************************************************************** * X11DRV_KEYBOARD_DetectLayout * @@ -1433,22 +1462,7 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) /* get data for keycode from X server */ for (i = 0; i < syms; i++) { if (!(keysym = XkbKeycodeToKeysym( display, keyc, 0, i ))) continue; - /* Allow both one-byte and two-byte national keysyms */ - if ((keysym < 0x8000) && (keysym != ' ')) - { - if (!XkbTranslateKeySym(display, &keysym, 0, &ckey[keyc][i], 1, NULL)) - { - TRACE("XKB could not translate keysym %04lx\n", keysym); - /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent - * with appropriate ShiftMask and Mode_switch, use XLookupString - * to get character in the local encoding. - */ - ckey[keyc][i] = keysym & 0xFF; - } - } - else { - ckey[keyc][i] = KEYBOARD_MapDeadKeysym(keysym); - } + ckey[keyc][i] = keysym_to_char(keysym); } } @@ -1623,20 +1637,8 @@ void X11DRV_InitKeyboard( Display *display ) /* we seem to need to search the layout-dependent scancodes */ int maxlen=0,maxval=-1,ok; for (i=0; i Date: Mon, 7 Dec 2020 09:49:53 +0100 Subject: [PATCH 330/758] winex11.drv: Dump keysyms and translations for all keys. Dump all we can see about the user keyboard, so that their +keyboard logs can be used to fix layout tables. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30984 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45605 CW-Bug-Id: #16793 --- dlls/winex11.drv/keyboard.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 9217ebb46ac..1d6cef40efb 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1463,6 +1463,19 @@ X11DRV_KEYBOARD_DetectLayout( Display *display ) for (i = 0; i < syms; i++) { if (!(keysym = XkbKeycodeToKeysym( display, keyc, 0, i ))) continue; ckey[keyc][i] = keysym_to_char(keysym); + if (TRACE_ON(keyboard)) + { + char buf[32]; + WCHAR bufW[32]; + int len, lenW; + KeySym orig_keysym = keysym; + len = XkbTranslateKeySym(display, &keysym, 0, buf, sizeof(buf), NULL); + lenW = ntdll_umbstowcs(buf, len, bufW, ARRAY_SIZE(bufW)); + if (lenW < ARRAY_SIZE(bufW)) + bufW[lenW] = 0; + TRACE("keycode %u, index %d, orig_keysym 0x%04lx, keysym 0x%04lx, buf %s, bufW %s\n", + keyc, i, orig_keysym, keysym, debugstr_a(buf), debugstr_w(bufW)); + } } } From 8f65131f10bbf5728f7ca0e5cb62251f0a28591d Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 8 Jun 2021 16:01:54 +0300 Subject: [PATCH 331/758] winex11.drv: Send missed KEYUP events on KeymapNotify. Full focus lost / focus gained events on the Windows side are not feasible for X11's FocusIn/FocusOut events generated by keyboard grabs (see XGrabKeyboard()) that are used for example for Atl+Tab handling. Using them would degrade user's experience, especially with our full screen hack, by causing the window to minimize or flash multiple times depending on a game/window manager combo. Because of that the programs may miss on some KEYUP events that happen during the grab, and since there are no focus changes on the Windows side the state doesn't get resynced. This change attempts to improve user experience by syncing any missed key release events that happened while the window haven't had focus on the X11 side. There's no syncing of key presses as those are more problematic because of window manager quirks, e.g. on KDE it may end up syncing the Tab press portion of Alt+Tab. Luckily missing key events for keys that were pressed and not released while the WM had the keyboard grab is not nearly as confusing as stuck keys. For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal and many other games that end up with stuck Alt after Alt+Tabbing. CW-Bug-ID: #17046 CW-Bug-ID: #18904 --- dlls/winex11.drv/event.c | 2 ++ dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- dlls/winex11.drv/mouse.c | 2 ++ dlls/winex11.drv/x11drv.h | 1 + 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index e760986807c..7656803180b 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -824,6 +824,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE; + x11drv_thread_data()->keymapnotify_hwnd = hwnd; + /* Focus was just restored but it can be right after super was * pressed and gnome-shell needs a bit of time to respond and * toggle the activity view. If we grab the cursor right away diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 1d6cef40efb..f476919f9f5 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1195,11 +1195,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) int i, j; BYTE keystate[256]; WORD vkey; + DWORD flags; + KeyCode keycode; + HWND keymapnotify_hwnd; BOOL changed = FALSE; struct { WORD vkey; + WORD scan; WORD pressed; } keys[256]; + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + + keymapnotify_hwnd = thread_data->keymapnotify_hwnd; + thread_data->keymapnotify_hwnd = NULL; if (!get_async_key_state( keystate )) return FALSE; @@ -1214,11 +1222,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) { for (j = 0; j < 8; j++) { - vkey = keyc2vkey[(i * 8) + j]; + keycode = (i * 8) + j; + vkey = keyc2vkey[keycode]; /* If multiple keys map to the same vkey, we want to report it as * pressed iff any of them are pressed. */ - if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; + if (!keys[vkey & 0xff].vkey) + { + keys[vkey & 0xff].vkey = vkey; + keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; + } + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + x11drv_thread_data()->keymapnotify_hwnd = hwnd; + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; /* simulate a mouse motion event */ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 6567eb91b2e..12a6cdefa82 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -385,6 +385,7 @@ struct x11drv_thread_data XEvent *current_event; /* event currently being processed */ HWND grab_hwnd; /* window that currently grabs the mouse */ HWND last_focus; /* last window that had focus */ + HWND keymapnotify_hwnd; /* window that should receive modifier release events */ XIM xim; /* input method */ HWND last_xic_hwnd; /* last xic window */ XFontSet font_set; /* international text drawing font set */ From 36d71e4e0298bcb11ee656c66ae436c692c93a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 12 Apr 2023 10:14:50 +0200 Subject: [PATCH 332/758] HACK: winex11: Create a dedicated thread for input events. CW-Bug-Id: #21879 --- dlls/winex11.drv/desktop.c | 5 +- dlls/winex11.drv/dllmain.c | 36 +++++++++++++ dlls/winex11.drv/event.c | 93 ++++++++++++++++++++++++++++++++++ dlls/winex11.drv/unixlib.h | 2 + dlls/winex11.drv/window.c | 7 ++- dlls/winex11.drv/x11drv.h | 5 ++ dlls/winex11.drv/x11drv_main.c | 11 ++++ 7 files changed, 153 insertions(+), 6 deletions(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index b7896632ca9..351552674e3 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -376,8 +376,9 @@ NTSTATUS x11drv_create_desktop( void *arg ) if (!wcsicmp( name, rootW )) return FALSE; /* Create window */ - win_attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask; + win_attr.event_mask = ExposureMask | FocusChangeMask | EnterWindowMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask; + if (!input_thread_hack) win_attr.event_mask |= KeyPressMask | KeyReleaseMask; win_attr.cursor = XCreateFontCursor( display, XC_top_left_arrow ); if (default_visual.visual != DefaultVisual( display, DefaultScreen(display) )) diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 3aa0a9875d0..f1553152dcf 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -22,6 +22,8 @@ #include "wine/debug.h" +WINE_DEFAULT_DEBUG_CHANNEL(x11drv); + HMODULE x11drv_module = 0; @@ -53,8 +55,29 @@ static const kernel_callback kernel_callbacks[] = C_ASSERT( NtUserDriverCallbackFirst + ARRAYSIZE(kernel_callbacks) == client_func_last ); +static DWORD CALLBACK input_thread( void *arg ) +{ + NTSTATUS status; + + SetThreadDescription( GetCurrentThread(), L"wine_x11drv_input" ); + + TRACE("\n"); + + /* wait for explorer startup sequence to complete */ + SendMessageW( GetDesktopWindow(), WM_NULL, 0, 0 ); + + for (;;) + { + status = X11DRV_CALL( input_thread, NULL ); + WARN( "input_thread returned %#lx\n", status ); + } + + return 0; +} + BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) { + static HANDLE thread; void **callback_table; struct init_params params = { @@ -62,6 +85,13 @@ BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) &show_systray, }; + if (reason == DLL_PROCESS_DETACH && !reserved && thread) + { + TerminateThread( thread, -1 ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + } + if (reason != DLL_PROCESS_ATTACH) return TRUE; DisableThreadLibraryCalls( instance ); @@ -69,6 +99,12 @@ BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) if (__wine_init_unix_call()) return FALSE; if (X11DRV_CALL( init, ¶ms )) return FALSE; + if (params.input_thread_hack) + { + thread = CreateThread( NULL, 0, input_thread, NULL, 0, NULL ); + if (!thread) ERR( "Failed to create input monitor thread, error %lu\n", GetLastError() ); + } + callback_table = NtCurrentTeb()->Peb->KernelCallbackTable; memcpy( callback_table + NtUserDriverCallbackFirst, kernel_callbacks, sizeof(kernel_callbacks) ); return TRUE; diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 7656803180b..91329a39167 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -147,6 +147,69 @@ static const char * event_names[MAX_EVENT_HANDLERS] = int xinput2_opcode = 0; +static pthread_mutex_t input_cs = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t input_cond = PTHREAD_COND_INITIALIZER; +static Display *input_display; + +/* wait for the input thread to startup and return the input display */ +static Display *x11drv_input_display(void) +{ + if (input_thread_hack && !input_display) + { + pthread_mutex_lock( &input_cs ); + while (!input_display) pthread_cond_wait( &input_cond, &input_cs ); + pthread_mutex_unlock( &input_cs ); + } + + return input_display; +} + +/* set the input display and notify waiters */ +static void x11drv_set_input_display( Display *display ) +{ + if (input_display) return; + + pthread_mutex_lock( &input_cs ); + input_display = display; + pthread_mutex_unlock( &input_cs ); + pthread_cond_broadcast( &input_cond ); +} + +/* add a window to the windows we get input for */ +void x11drv_input_add_window( HWND hwnd, Window window ) +{ + long mask = KeyPressMask | KeyReleaseMask | KeymapStateMask; + Display *display = x11drv_input_display(); + + if (!input_thread_hack) return; + + TRACE( "display %p, window %p/%lx\n", display, hwnd, window ); + + pthread_mutex_lock( &input_cs ); + XSaveContext( display, window, winContext, (char *)hwnd ); + pthread_mutex_unlock( &input_cs ); + + XSelectInput( display, window, mask ); + XFlush( display ); +} + +/* remove a window from the windows we get input for */ +void x11drv_input_remove_window( Window window ) +{ + Display *display = x11drv_input_display(); + + if (!input_thread_hack) return; + + TRACE( "display %p, window %lx\n", display, window ); + + XSelectInput( display, window, 0 ); + XFlush( display ); + + pthread_mutex_lock( &input_cs ); + XDeleteContext( display, window, winContext ); + pthread_mutex_unlock( &input_cs ); +} + /* return the name of an X event */ static const char *dbgstr_event( int type ) { @@ -421,11 +484,13 @@ static inline BOOL call_event_handler( Display *display, XEvent *event ) return FALSE; /* no handler, ignore it */ } + pthread_mutex_lock( &input_cs ); #ifdef GenericEvent if (event->type == GenericEvent) hwnd = 0; else #endif if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0) hwnd = 0; /* not for a registered window */ + pthread_mutex_unlock( &input_cs ); if (!hwnd && event->xany.window == root_window) hwnd = NtUserGetDesktopWindow(); TRACE( "%lu %s for hwnd/window %p/%lx\n", @@ -460,6 +525,15 @@ static BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,X prev_event.type = 0; while (XCheckIfEvent( display, &event, filter, (char *)arg )) { + switch (event.type) + { + case KeyPress: + case KeyRelease: + case KeymapNotify: + if (input_thread_hack && display != x11drv_input_display()) continue; + break; + } + count++; if (overlay_enabled && filter_event( display, &event, (char *)overlay_filter )) continue; if (steam_keyboard_opened && filter_event( display, &event, (char *)keyboard_filter )) continue; @@ -1055,6 +1129,8 @@ static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event ) { struct x11drv_win_data *data; + x11drv_input_add_window( hwnd, event->xany.window ); + if (event->xany.window == x11drv_thread_data()->clip_window) return TRUE; if (!(data = get_win_data( hwnd ))) return FALSE; @@ -1075,6 +1151,7 @@ static BOOL X11DRV_MapNotify( HWND hwnd, XEvent *event ) */ static BOOL X11DRV_UnmapNotify( HWND hwnd, XEvent *event ) { + x11drv_input_remove_window( event->xany.window ); return TRUE; } @@ -2098,3 +2175,19 @@ static BOOL X11DRV_ClientMessage( HWND hwnd, XEvent *xev ) TRACE( "no handler found for %ld\n", event->message_type ); return FALSE; } + +NTSTATUS x11drv_input_thread( void *arg ) +{ + struct x11drv_thread_data *data = x11drv_init_thread_data(); + + x11drv_set_input_display( data->display ); + + for (;;) + { + XEvent event; + XPeekEvent( data->display, &event ); + process_events( data->display, filter_event, QS_ALLINPUT ); + } + + return 0; +} diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 0c812896fbc..45377effdc7 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -31,6 +31,7 @@ enum x11drv_funcs unix_tablet_get_packet, unix_tablet_info, unix_tablet_load_info, + unix_input_thread, unix_funcs_count, }; @@ -48,6 +49,7 @@ struct init_params { WNDPROC foreign_window_proc; BOOL *show_systray; + BOOL input_thread_hack; }; struct systray_dock_params diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 417a51d56f1..08656010c2f 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -453,11 +453,10 @@ static int get_window_attributes( struct x11drv_win_data *data, XSetWindowAttrib attr->bit_gravity = NorthWestGravity; attr->backing_store = NotUseful; attr->border_pixel = 0; - attr->event_mask = (ExposureMask | PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | EnterWindowMask | - KeyPressMask | KeyReleaseMask | FocusChangeMask | - KeymapStateMask | StructureNotifyMask); + attr->event_mask = (ExposureMask | FocusChangeMask | StructureNotifyMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask); if (data->managed) attr->event_mask |= PropertyChangeMask; + if (!input_thread_hack) attr->event_mask |= KeyPressMask | KeyReleaseMask | KeymapStateMask; return (CWOverrideRedirect | CWSaveUnder | CWColormap | CWBorderPixel | CWEventMask | CWBitGravity | CWBackingStore); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 12a6cdefa82..e6fd5ef940b 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -599,6 +599,9 @@ extern void (*pXFreeEventData)( Display *display, XEvent /*XGenericEventCookie*/ extern DWORD x11drv_time_to_ticks(Time time) DECLSPEC_HIDDEN; +extern void x11drv_input_add_window( HWND hwnd, Window window ) DECLSPEC_HIDDEN; +extern void x11drv_input_remove_window( Window window ) DECLSPEC_HIDDEN; + /* X11 driver private messages, must be in the range 0x80001000..0x80001fff */ enum x11drv_window_messages { @@ -907,6 +910,7 @@ extern NTSTATUS x11drv_tablet_attach_queue( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_tablet_get_packet( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_tablet_load_info( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_tablet_info( void *arg ) DECLSPEC_HIDDEN; +extern NTSTATUS x11drv_input_thread( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_client_func( enum x11drv_client_funcs func, const void *params, ULONG size ) DECLSPEC_HIDDEN; @@ -1017,5 +1021,6 @@ static inline UINT asciiz_to_unicode( WCHAR *dst, const char *src ) extern BOOL layered_window_client_hack; extern BOOL vulkan_gdi_blit_source_hack; extern BOOL vulkan_disable_child_window_rendering_hack; +extern BOOL input_thread_hack; #endif /* __WINE_X11DRV_H */ diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 0366ed51d8d..923b0037217 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -97,6 +97,7 @@ HANDLE steam_keyboard_event; BOOL layered_window_client_hack = FALSE; BOOL vulkan_gdi_blit_source_hack = FALSE; BOOL vulkan_disable_child_window_rendering_hack = FALSE; +BOOL input_thread_hack = FALSE; static x11drv_error_callback err_callback; /* current callback for error */ static Display *err_callback_display; /* display callback is set for */ @@ -859,11 +860,19 @@ static NTSTATUS x11drv_init( void *arg ) !strcmp(sgi, "1009290") /* Bug 21949 : SWORD ART ONLINE Alicization Lycoris video tearing */ )) || (e && *e != '\0' && *e != '0'); + + e = getenv("WINE_INPUT_THREAD_HACK"); + input_thread_hack = + (sgi && ( + !strcmp(sgi, "1938010") + )) || + (e && *e != '\0' && *e != '0'); } init_user_driver(); X11DRV_DisplayDevices_Init(FALSE); *params->show_systray = show_systray; + params->input_thread_hack = input_thread_hack; return STATUS_SUCCESS; } @@ -1486,6 +1495,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = x11drv_tablet_get_packet, x11drv_tablet_info, x11drv_tablet_load_info, + x11drv_input_thread, }; @@ -1574,6 +1584,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = x11drv_wow64_tablet_get_packet, x11drv_wow64_tablet_info, x11drv_tablet_load_info, + x11drv_input_thread, }; C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count ); From 10ba8a3a9a082cce4844a77b7b11e330fe626369 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 7 Aug 2023 21:49:05 +0300 Subject: [PATCH 333/758] Revert "winegstreamer: Free the media source work queue outside of the CS." This reverts commit df2606689a51737200cbac94a30a63a368747459. --- dlls/winegstreamer/media_source.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index c9ee6f227e6..ec201c5437b 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1236,7 +1236,6 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) if (!ref) { IMFMediaSource_Shutdown(iface); - MFUnlockWorkQueue(source->async_commands_queue); IMFPresentationDescriptor_Release(source->pres_desc); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); @@ -1438,6 +1437,8 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) } free(source->streams); + MFUnlockWorkQueue(source->async_commands_queue); + LeaveCriticalSection(&source->cs); return S_OK; From 59ae32e0cbba948c3241f23321e01047d0ea87dd Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 7 Aug 2023 21:49:19 +0300 Subject: [PATCH 334/758] Revert "fixup! winegstreamer: Allow concurrent wait_on_sample in the media source." This reverts commit 5034527cb3573fadb11587949ba41ab4d7ab62be. --- dlls/winegstreamer/media_source.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ec201c5437b..6c00f103662 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -44,12 +44,11 @@ struct media_stream LONG token_queue_count; LONG token_queue_cap; + CRITICAL_SECTION cs; + DWORD stream_id; BOOL active; BOOL eos; - - DWORD busy; - CONDITION_VARIABLE cond; }; enum source_async_op @@ -539,12 +538,17 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) TRACE("%p, %p\n", stream, token); - stream->busy = TRUE; + EnterCriticalSection(&stream->cs); + + if (!stream->wg_stream) + { + LeaveCriticalSection(&stream->cs); + return; + } + LeaveCriticalSection(&source->cs); ret = wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer); EnterCriticalSection(&source->cs); - stream->busy = FALSE; - WakeConditionVariable(&stream->cond); if (source->state == SOURCE_SHUTDOWN) WARN("media source has been shutdown, returning\n"); @@ -556,6 +560,8 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); dispatch_end_of_presentation(source); } + + LeaveCriticalSection(&stream->cs); } static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -715,6 +721,8 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) IMFStreamDescriptor_Release(stream->descriptor); IMFMediaEventQueue_Release(stream->event_queue); flush_token_queue(stream, FALSE); + stream->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&stream->cs); free(stream); } @@ -879,6 +887,9 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, object->eos = FALSE; object->wg_stream = wg_parser_get_stream(wg_parser, id); + InitializeCriticalSection(&object->cs); + object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + TRACE("Created stream object %p.\n", object); *out = object; @@ -1416,8 +1427,10 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_stream *stream = source->streams[i]; wg_parser_stream_disable(stream->wg_stream); - while (stream->busy) - SleepConditionVariableCS(&stream->cond, &source->cs, INFINITE); + + EnterCriticalSection(&stream->cs); + stream->wg_stream = NULL; + LeaveCriticalSection(&stream->cs); } wg_parser_disconnect(source->wg_parser); From 15c13cfe0aee8017c680e8b2b4a2e546e233eabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 5 Jul 2023 21:41:58 +0200 Subject: [PATCH 335/758] Revert "winegstreamer: Allow concurrent wait_on_sample in the media source." This reverts commit d0cb048bfdedc77164a1a4451b6ceeb8ba1a493a. --- dlls/winegstreamer/media_source.c | 37 ++----------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 6c00f103662..e6e78fd4de3 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -44,8 +44,6 @@ struct media_stream LONG token_queue_count; LONG token_queue_cap; - CRITICAL_SECTION cs; - DWORD stream_id; BOOL active; BOOL eos; @@ -534,34 +532,19 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) struct media_source *source = impl_from_IMFMediaSource(stream->media_source); PROPVARIANT empty_var = {.vt = VT_EMPTY}; struct wg_parser_buffer buffer; - BOOL ret; TRACE("%p, %p\n", stream, token); - EnterCriticalSection(&stream->cs); - - if (!stream->wg_stream) + if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) { - LeaveCriticalSection(&stream->cs); - return; - } - - LeaveCriticalSection(&source->cs); - ret = wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer); - EnterCriticalSection(&source->cs); - - if (source->state == SOURCE_SHUTDOWN) - WARN("media source has been shutdown, returning\n"); - else if (ret) send_buffer(stream, &buffer, token); + } else { stream->eos = TRUE; IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); dispatch_end_of_presentation(source); } - - LeaveCriticalSection(&stream->cs); } static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -721,8 +704,6 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) IMFStreamDescriptor_Release(stream->descriptor); IMFMediaEventQueue_Release(stream->event_queue); flush_token_queue(stream, FALSE); - stream->cs.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&stream->cs); free(stream); } @@ -887,9 +868,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, object->eos = FALSE; object->wg_stream = wg_parser_get_stream(wg_parser, id); - InitializeCriticalSection(&object->cs); - object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); - TRACE("Created stream object %p.\n", object); *out = object; @@ -1409,7 +1387,6 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *source = impl_from_IMFMediaSource(iface); - UINT i; TRACE("%p.\n", iface); @@ -1423,16 +1400,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) source->state = SOURCE_SHUTDOWN; - for (i = 0; i < source->stream_count; i++) - { - struct media_stream *stream = source->streams[i]; - wg_parser_stream_disable(stream->wg_stream); - - EnterCriticalSection(&stream->cs); - stream->wg_stream = NULL; - LeaveCriticalSection(&stream->cs); - } - wg_parser_disconnect(source->wg_parser); source->read_thread_shutdown = true; From 17d8f46e3a39e7a42b7b7d1e7792e09daa84fd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 14 Apr 2023 15:19:52 +0200 Subject: [PATCH 336/758] Revert "winegstreamer: add http/https/rtsp scheme handler" This reverts commit 43109ae880034dd9e8c7053ef8fa596931eea3d8. --- dlls/winegstreamer/Makefile.in | 1 - dlls/winegstreamer/gst_private.h | 2 - dlls/winegstreamer/mfplat.c | 3 - dlls/winegstreamer/scheme_handler.c | 408 ------------------- dlls/winegstreamer/winegstreamer.rgs | 28 -- dlls/winegstreamer/winegstreamer_classes.idl | 7 - 6 files changed, 449 deletions(-) delete mode 100644 dlls/winegstreamer/scheme_handler.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index ea232db3ff1..39faf453fe8 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -16,7 +16,6 @@ C_SRCS = \ quartz_parser.c \ quartz_transform.c \ resampler.c \ - scheme_handler.c \ unixlib.c \ video_decoder.c \ video_processor.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e4d0af8af44..ab489ea7d20 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -149,12 +149,10 @@ HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample, HRESULT wg_transform_read_quartz(struct wg_transform *transform, struct wg_sample *sample); HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj); -HRESULT winegstreamer_create_media_source_from_uri(const WCHAR *uri, IUnknown **out_media_source) DECLSPEC_HIDDEN; HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); HRESULT video_processor_create(REFIID riid, void **ret); -HRESULT gstreamer_scheme_handler_construct(REFIID riid, void **ret) DECLSPEC_HIDDEN; extern const GUID MFAudioFormat_RAW_AAC; diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index c1deffbb1cc..ea5ea33ee9d 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -122,8 +122,6 @@ static const IClassFactoryVtbl class_factory_vtbl = static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; -static const GUID CLSID_GStreamerSchemePlugin = {0x587eeb6a,0x7336,0x4ebd,{0xa4,0xf2,0x91,0xc9,0x48,0xde,0x62,0x2c}}; - static const struct class_object { const GUID *clsid; @@ -135,7 +133,6 @@ class_objects[] = { &CLSID_GStreamerByteStreamHandler, &winegstreamer_stream_handler_create }, { &CLSID_MSAACDecMFT, &aac_decoder_create }, { &CLSID_MSH264DecoderMFT, &h264_decoder_create }, - { &CLSID_GStreamerSchemePlugin, &gstreamer_scheme_handler_construct }, }; HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) diff --git a/dlls/winegstreamer/scheme_handler.c b/dlls/winegstreamer/scheme_handler.c deleted file mode 100644 index 1aa36b8b7a5..00000000000 --- a/dlls/winegstreamer/scheme_handler.c +++ /dev/null @@ -1,408 +0,0 @@ -#include - -#define COBJMACROS - -#include "windef.h" -#include "winbase.h" -#include "mfidl.h" -#include "mferror.h" -#include "mfapi.h" -#include "gst_private.h" - -#include "wine/debug.h" -#include "wine/list.h" - -WINE_DEFAULT_DEBUG_CHANNEL(mfplat); - -struct gstreamer_scheme_handler_result -{ - struct list entry; - IMFAsyncResult *result; - IUnknown *object; -}; - -struct gstreamer_scheme_handler -{ - IMFSchemeHandler IMFSchemeHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; - LONG refcount; - struct list results; - CRITICAL_SECTION cs; -}; - -static struct gstreamer_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) -{ - return CONTAINING_RECORD(iface, struct gstreamer_scheme_handler, IMFSchemeHandler_iface); -} - -static struct gstreamer_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct gstreamer_scheme_handler, IMFAsyncCallback_iface); -} - -static HRESULT WINAPI gstreamer_scheme_handler_QueryIntace(IMFSchemeHandler *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IMFSchemeHandler) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFSchemeHandler_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI gstreamer_scheme_handler_AddRef(IMFSchemeHandler *iface) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - ULONG refcount = InterlockedIncrement(&handler->refcount); - - TRACE("%p, refcount %lu.\n", handler, refcount); - - return refcount; -} - -static ULONG WINAPI gstreamer_scheme_handler_Release(IMFSchemeHandler *iface) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct gstreamer_scheme_handler_result *result, *next; - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct gstreamer_scheme_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - free(result); - } - DeleteCriticalSection(&handler->cs); - free(handler); - } - - return refcount; -} - -struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - WCHAR *url; - DWORD flags; -}; - -static struct create_object_context *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); -} - -static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedIncrement(&context->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI create_object_context_Release(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(&context->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - if (context->props) - IPropertyStore_Release(context->props); - free(context->url); - free(context); - } - - return refcount; -} - -static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static HRESULT WINAPI gstreamer_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags, - IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr; - - TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); - - if (cancel_cookie) - *cancel_cookie = NULL; - - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) - return hr; - - if (!(context = malloc(sizeof(*context)))) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; - context->refcount = 1; - context->props = props; - if (context->props) - IPropertyStore_AddRef(context->props); - context->flags = flags; - context->url = wcsdup(url); - if (!context->url) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item); - IUnknown_Release(&context->IUnknown_iface); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - { - *cancel_cookie = (IUnknown *)caller; - IUnknown_AddRef(*cancel_cookie); - } - } - - IMFAsyncResult_Release(item); - } - IMFAsyncResult_Release(caller); - - return hr; -} - -static HRESULT WINAPI gstreamer_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result, - MF_OBJECT_TYPE *obj_type, IUnknown **object) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct gstreamer_scheme_handler_result *found = NULL, *cur; - HRESULT hr; - - TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct gstreamer_scheme_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - *obj_type = MF_OBJECT_MEDIASOURCE; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - free(found); - } - else - { - *obj_type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; -} - -static HRESULT WINAPI gstreamer_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct gstreamer_scheme_handler_result *found = NULL, *cur; - - TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct gstreamer_scheme_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; -} - -static const IMFSchemeHandlerVtbl gstreamer_scheme_handler_vtbl = -{ - gstreamer_scheme_handler_QueryIntace, - gstreamer_scheme_handler_AddRef, - gstreamer_scheme_handler_Release, - gstreamer_scheme_handler_BeginCreateObject, - gstreamer_scheme_handler_EndCreateObject, - gstreamer_scheme_handler_CancelObjectCreation, -}; - -static HRESULT WINAPI gstreamer_scheme_handler_callback_QueryIntace(IMFAsyncCallback *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFAsyncCallback) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFAsyncCallback_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI gstreamer_scheme_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface); -} - -static ULONG WINAPI gstreamer_scheme_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct gstreamer_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); -} - -static HRESULT WINAPI gstreamer_scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - -static HRESULT WINAPI gstreamer_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) -{ - IMFAsyncResult *caller; - struct gstreamer_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - struct gstreamer_scheme_handler_result *handler_result; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; - HRESULT hr; - - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); - - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) - { - WARN("Expected context set for callee result.\n"); - return hr; - } - - context = impl_from_IUnknown(context_object); - - hr = winegstreamer_create_media_source_from_uri(context->url, &object); - - handler_result = malloc(sizeof(*handler_result)); - if (handler_result) - { - handler_result->result = caller; - IMFAsyncResult_AddRef(handler_result->result); - - handler_result->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &handler_result->entry); - LeaveCriticalSection(&handler->cs); - } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - - return S_OK; -} - -static const IMFAsyncCallbackVtbl gstreamer_scheme_handler_callback_vtbl = -{ - gstreamer_scheme_handler_callback_QueryIntace, - gstreamer_scheme_handler_callback_AddRef, - gstreamer_scheme_handler_callback_Release, - gstreamer_scheme_handler_callback_GetParameters, - gstreamer_scheme_handler_callback_Invoke, -}; - -HRESULT gstreamer_scheme_handler_construct(REFIID riid, void **obj) -{ - struct gstreamer_scheme_handler *handler; - HRESULT hr; - - TRACE("%s, %p.\n", debugstr_guid(riid), obj); - - if (!(handler = calloc(1, sizeof(*handler)))) - return E_OUTOFMEMORY; - - handler->IMFSchemeHandler_iface.lpVtbl = &gstreamer_scheme_handler_vtbl; - handler->IMFAsyncCallback_iface.lpVtbl = &gstreamer_scheme_handler_callback_vtbl; - handler->refcount = 1; - list_init(&handler->results); - InitializeCriticalSection(&handler->cs); - - hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); - IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); - - return hr; -} - diff --git a/dlls/winegstreamer/winegstreamer.rgs b/dlls/winegstreamer/winegstreamer.rgs index c50d3a05747..923ba673f8c 100644 --- a/dlls/winegstreamer/winegstreamer.rgs +++ b/dlls/winegstreamer/winegstreamer.rgs @@ -12,31 +12,3 @@ HKCR } } } - -HKLM -{ - NoRemove 'Software' - { - NoRemove 'Microsoft' - { - NoRemove 'Windows Media Foundation' - { - NoRemove 'SchemeHandlers' - { - 'http:' - { - val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler' - } - 'https:' - { - val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler' - } - 'rtsp:' - { - val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler' - } - } - } - } - } -} diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 1b87dcde716..30a99c9acfb 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -111,10 +111,3 @@ coclass CResamplerMediaObject {} uuid(98230571-0087-4204-b020-3282538e57d3) ] coclass CColorConvertDMO {} - -[ - helpstring("GStreamer scheme handler"), - threading(both), - uuid(587eeb6a-7336-4ebd-a4f2-91c948de622c) -] -coclass GStreamerSchemePlugin { } From ff7764ae40bbf997228debac7f5b1c5c7e38ec85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:39:18 +0200 Subject: [PATCH 337/758] winegstreamer: Move GStreamerByteStreamHandler to mf_handler.c. CW-Bug-Id: #21953 --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 463 +---------------------------- dlls/winegstreamer/mf_handler.c | 466 ++++++++++++++++++++++++++++++ 4 files changed, 470 insertions(+), 461 deletions(-) create mode 100644 dlls/winegstreamer/mf_handler.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 39faf453fe8..49b4dd2c631 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ h264_decoder.c \ main.c \ media_source.c \ + mf_handler.c \ mfplat.c \ quartz_parser.c \ quartz_transform.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index ab489ea7d20..fde5c231c07 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -149,6 +149,7 @@ HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample, HRESULT wg_transform_read_quartz(struct wg_transform *transform, struct wg_sample *sample); HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj); +HRESULT media_source_create_from_stream(IMFByteStream *stream, IMFMediaSource **out); HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e6e78fd4de3..78e507347a6 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1441,7 +1441,7 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; -static HRESULT media_source_constructor(IMFByteStream *bytestream, const WCHAR *uri, struct media_source **out_media_source) +HRESULT media_source_create_from_stream(IMFByteStream *bytestream, IMFMediaSource **out) { BOOL video_selected = FALSE, audio_selected = FALSE; IMFStreamDescriptor **descriptors = NULL; @@ -1608,7 +1608,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, const WCHAR * object->state = SOURCE_STOPPED; - *out_media_source = object; + *out = &object->IMFMediaSource_iface; return S_OK; fail: @@ -1646,462 +1646,3 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, const WCHAR * free(object); return hr; } - -HRESULT winegstreamer_create_media_source_from_uri(const WCHAR *uri, IUnknown **out_object) -{ - struct media_source *object; - IMFByteStream *bytestream; - IStream *stream; - HRESULT hr; - - if (FAILED(hr = CreateStreamOnHGlobal(0, TRUE, &stream))) - return hr; - - hr = MFCreateMFByteStreamOnStream(stream, &bytestream); - IStream_Release(stream); - if (FAILED(hr)) - return hr; - - if (SUCCEEDED(hr = media_source_constructor(bytestream, uri, &object))) - *out_object = (IUnknown*)&object->IMFMediaSource_iface; - - IMFByteStream_Release(bytestream); - return hr; -} - -struct winegstreamer_stream_handler_result -{ - struct list entry; - IMFAsyncResult *result; - MF_OBJECT_TYPE obj_type; - IUnknown *object; -}; - -struct winegstreamer_stream_handler -{ - IMFByteStreamHandler IMFByteStreamHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; - LONG refcount; - struct list results; - CRITICAL_SECTION cs; -}; - -static struct winegstreamer_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) -{ - return CONTAINING_RECORD(iface, struct winegstreamer_stream_handler, IMFByteStreamHandler_iface); -} - -static struct winegstreamer_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct winegstreamer_stream_handler, IMFAsyncCallback_iface); -} - -static HRESULT WINAPI winegstreamer_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IMFByteStreamHandler) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFByteStreamHandler_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI winegstreamer_stream_handler_AddRef(IMFByteStreamHandler *iface) -{ - struct winegstreamer_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); - ULONG refcount = InterlockedIncrement(&handler->refcount); - - TRACE("%p, refcount %lu.\n", handler, refcount); - - return refcount; -} - -static ULONG WINAPI winegstreamer_stream_handler_Release(IMFByteStreamHandler *iface) -{ - struct winegstreamer_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct winegstreamer_stream_handler_result *result, *next; - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct winegstreamer_stream_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - free(result); - } - DeleteCriticalSection(&handler->cs); - free(handler); - } - - return refcount; -} - -struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - IMFByteStream *stream; - WCHAR *url; - DWORD flags; -}; - -static struct create_object_context *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); -} - -static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedIncrement(&context->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI create_object_context_Release(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(&context->refcount); - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - if (context->props) - IPropertyStore_Release(context->props); - if (context->stream) - IMFByteStream_Release(context->stream); - free(context->url); - free(context); - } - - return refcount; -} - -static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static HRESULT WINAPI winegstreamer_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, - IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) -{ - struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr; - - TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); - - if (cancel_cookie) - *cancel_cookie = NULL; - - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) - return hr; - - if (!(context = calloc(1, sizeof(*context)))) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; - context->refcount = 1; - context->props = props; - if (context->props) - IPropertyStore_AddRef(context->props); - context->flags = flags; - context->stream = stream; - if (context->stream) - IMFByteStream_AddRef(context->stream); - if (url) - context->url = wcsdup(url); - if (!context->stream) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); - IUnknown_Release(&context->IUnknown_iface); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - { - *cancel_cookie = (IUnknown *)caller; - IUnknown_AddRef(*cancel_cookie); - } - } - - IMFAsyncResult_Release(item); - } - IMFAsyncResult_Release(caller); - - return hr; -} - -static HRESULT WINAPI winegstreamer_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, - MF_OBJECT_TYPE *obj_type, IUnknown **object) -{ - struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct winegstreamer_stream_handler_result *found = NULL, *cur; - HRESULT hr; - - TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&this->cs); - - LIST_FOR_EACH_ENTRY(cur, &this->results, struct winegstreamer_stream_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&this->cs); - - if (found) - { - *obj_type = found->obj_type; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - free(found); - } - else - { - *obj_type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; -} - -static HRESULT WINAPI winegstreamer_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) -{ - struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct winegstreamer_stream_handler_result *found = NULL, *cur; - - TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&this->cs); - - LIST_FOR_EACH_ENTRY(cur, &this->results, struct winegstreamer_stream_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&this->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; -} - -static HRESULT WINAPI winegstreamer_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) -{ - FIXME("stub (%p %p)\n", iface, bytes); - return E_NOTIMPL; -} - -static const IMFByteStreamHandlerVtbl winegstreamer_stream_handler_vtbl = -{ - winegstreamer_stream_handler_QueryInterface, - winegstreamer_stream_handler_AddRef, - winegstreamer_stream_handler_Release, - winegstreamer_stream_handler_BeginCreateObject, - winegstreamer_stream_handler_EndCreateObject, - winegstreamer_stream_handler_CancelObjectCreation, - winegstreamer_stream_handler_GetMaxNumberOfBytesRequiredForResolution, -}; - -static HRESULT WINAPI winegstreamer_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IMFAsyncCallback) || - IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IMFAsyncCallback_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI winegstreamer_stream_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); -} - -static ULONG WINAPI winegstreamer_stream_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); -} - -static HRESULT WINAPI winegstreamer_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - -static HRESULT winegstreamer_stream_handler_create_object(struct winegstreamer_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, - IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) -{ - TRACE("%p, %s, %p, %#lx, %p, %p, %p.\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); - - if (flags & MF_RESOLUTION_MEDIASOURCE) - { - HRESULT hr; - struct media_source *new_source; - - if (FAILED(hr = media_source_constructor(stream, NULL, &new_source))) - return hr; - - TRACE("->(%p)\n", new_source); - - *out_object = (IUnknown*)&new_source->IMFMediaSource_iface; - *out_obj_type = MF_OBJECT_MEDIASOURCE; - - return S_OK; - } - else - { - FIXME("Unhandled flags %#lx.\n", flags); - return E_NOTIMPL; - } -} - -static HRESULT WINAPI winegstreamer_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) -{ - struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - struct winegstreamer_stream_handler_result *handler_result; - MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; - IMFAsyncResult *caller; - HRESULT hr; - - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); - - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) - { - WARN("Expected context set for callee result.\n"); - return hr; - } - - context = impl_from_IUnknown(context_object); - - hr = winegstreamer_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); - - if ((handler_result = malloc(sizeof(*handler_result)))) - { - handler_result->result = caller; - IMFAsyncResult_AddRef(handler_result->result); - handler_result->obj_type = obj_type; - handler_result->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &handler_result->entry); - LeaveCriticalSection(&handler->cs); - } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IUnknown_Release(&context->IUnknown_iface); - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - - return S_OK; -} - -static const IMFAsyncCallbackVtbl winegstreamer_stream_handler_callback_vtbl = -{ - winegstreamer_stream_handler_callback_QueryInterface, - winegstreamer_stream_handler_callback_AddRef, - winegstreamer_stream_handler_callback_Release, - winegstreamer_stream_handler_callback_GetParameters, - winegstreamer_stream_handler_callback_Invoke, -}; - -HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) -{ - struct winegstreamer_stream_handler *this; - HRESULT hr; - - TRACE("%s, %p.\n", debugstr_guid(riid), obj); - - if (!(this = calloc(1, sizeof(*this)))) - return E_OUTOFMEMORY; - - list_init(&this->results); - InitializeCriticalSection(&this->cs); - - this->IMFByteStreamHandler_iface.lpVtbl = &winegstreamer_stream_handler_vtbl; - this->IMFAsyncCallback_iface.lpVtbl = &winegstreamer_stream_handler_callback_vtbl; - this->refcount = 1; - - hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj); - IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface); - - return hr; -} diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c new file mode 100644 index 00000000000..ffb12be38e3 --- /dev/null +++ b/dlls/winegstreamer/mf_handler.c @@ -0,0 +1,466 @@ +/* + * Copyright 2020 Derek Lesho + * Copyright 2020 Zebediah Figura 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 + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "mfidl.h" +#include "mferror.h" +#include "mfapi.h" +#include "gst_private.h" + +#include "wine/debug.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct result_entry +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE type; + IUnknown *object; +}; + +struct async_create_object +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +static struct async_create_object *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct async_create_object, IUnknown_iface); +} + +static HRESULT WINAPI async_create_object_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_create_object_AddRef(IUnknown *iface) +{ + struct async_create_object *async = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&async->refcount); + TRACE("%p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI async_create_object_Release(IUnknown *iface) +{ + struct async_create_object *async = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&async->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + if (async->props) + IPropertyStore_Release(async->props); + if (async->stream) + IMFByteStream_Release(async->stream); + free(async->url); + free(async); + } + + return refcount; +} + +static const IUnknownVtbl async_create_object_vtbl = +{ + async_create_object_QueryInterface, + async_create_object_AddRef, + async_create_object_Release, +}; + +struct handler +{ + IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct handler, IMFAsyncCallback_iface); +} + +static struct handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) +{ + return CONTAINING_RECORD(iface, struct handler, IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI async_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) + || IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_callback_AddRef(IMFAsyncCallback *iface) +{ + struct handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); +} + +static ULONG WINAPI async_callback_Release(IMFAsyncCallback *iface) +{ + struct handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI async_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT stream_handler_create_object(struct handler *handler, WCHAR *url, + IMFByteStream *stream, DWORD flags, IPropertyStore *props, IUnknown **object, MF_OBJECT_TYPE *type) +{ + TRACE("%p, %s, %p, %#lx, %p, %p, %p.\n", handler, debugstr_w(url), stream, flags, props, object, type); + + if (flags & MF_RESOLUTION_MEDIASOURCE) + { + HRESULT hr; + + if (FAILED(hr = media_source_create_from_stream(stream, (IMFMediaSource **)object))) + return hr; + + *type = MF_OBJECT_MEDIASOURCE; + return S_OK; + } + else + { + FIXME("Unhandled flags %#lx.\n", flags); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI async_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct handler *handler = impl_from_IMFAsyncCallback(iface); + MF_OBJECT_TYPE type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct async_create_object *async; + struct result_entry *entry; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + async = impl_from_IUnknown(context_object); + + hr = stream_handler_create_object(handler, async->url, async->stream, async->flags, + async->props, &object, &type); + + if ((entry = malloc(sizeof(*entry)))) + { + entry->result = caller; + IMFAsyncResult_AddRef(entry->result); + entry->type = type; + entry->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &entry->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&async->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl async_callback_vtbl = +{ + async_callback_QueryInterface, + async_callback_AddRef, + async_callback_Release, + async_callback_GetParameters, + async_callback_Invoke, +}; + +static HRESULT WINAPI stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFByteStreamHandler) + || IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFByteStreamHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI stream_handler_AddRef(IMFByteStreamHandler *iface) +{ + struct handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + TRACE("%p, refcount %lu.\n", handler, refcount); + return refcount; +} + +static ULONG WINAPI stream_handler_Release(IMFByteStreamHandler *iface) +{ + struct handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct result_entry *entry, *next; + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &handler->results, struct result_entry, entry) + { + list_remove(&entry->entry); + IMFAsyncResult_Release(entry->result); + if (entry->object) + IUnknown_Release(entry->object); + free(entry); + } + DeleteCriticalSection(&handler->cs); + free(handler); + } + + return refcount; +} + +static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, + IMFByteStream *stream, const WCHAR *url, DWORD flags, IPropertyStore *props, + IUnknown **cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct handler *handler = impl_from_IMFByteStreamHandler(iface); + struct async_create_object *async; + IMFAsyncResult *caller, *item; + HRESULT hr; + + TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cookie, callback, state); + + if (cookie) + *cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + if (!(async = calloc(1, sizeof(*async)))) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + async->IUnknown_iface.lpVtbl = &async_create_object_vtbl; + async->refcount = 1; + async->props = props; + if (async->props) + IPropertyStore_AddRef(async->props); + async->flags = flags; + async->stream = stream; + if (async->stream) + IMFByteStream_AddRef(async->stream); + if (url) + async->url = wcsdup(url); + if (!async->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&async->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&async->IUnknown_iface, &handler->IMFAsyncCallback_iface, + (IUnknown *)caller, &item); + IUnknown_Release(&async->IUnknown_iface); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cookie) + { + *cookie = (IUnknown *)caller; + IUnknown_AddRef(*cookie); + } + } + + IMFAsyncResult_Release(item); + } + IMFAsyncResult_Release(caller); + + return hr; +} + +static HRESULT WINAPI stream_handler_EndCreateObject(IMFByteStreamHandler *iface, + IMFAsyncResult *result, MF_OBJECT_TYPE *type, IUnknown **object) +{ + struct handler *handler = impl_from_IMFByteStreamHandler(iface); + struct result_entry *found = NULL, *entry; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, type, object); + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(entry, &handler->results, struct result_entry, entry) + { + if (result == entry->result) + { + list_remove(&entry->entry); + found = entry; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + *type = found->type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + free(found); + } + else + { + *type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT WINAPI stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cookie) +{ + struct handler *handler = impl_from_IMFByteStreamHandler(iface); + struct result_entry *found = NULL, *entry; + + TRACE("%p, %p.\n", iface, cookie); + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(entry, &handler->results, struct result_entry, entry) + { + if (cookie == (IUnknown *)entry->result) + { + list_remove(&entry->entry); + found = entry; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +static HRESULT WINAPI stream_handler_GetMaxNumberOfBytesRequiredForResolution( + IMFByteStreamHandler *iface, QWORD *bytes) +{ + FIXME("stub (%p %p)\n", iface, bytes); + return E_NOTIMPL; +} + +static const IMFByteStreamHandlerVtbl stream_handler_vtbl = +{ + stream_handler_QueryInterface, + stream_handler_AddRef, + stream_handler_Release, + stream_handler_BeginCreateObject, + stream_handler_EndCreateObject, + stream_handler_CancelObjectCreation, + stream_handler_GetMaxNumberOfBytesRequiredForResolution, +}; + +HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) +{ + struct handler *handler; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(handler = calloc(1, sizeof(*handler)))) + return E_OUTOFMEMORY; + + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); + + handler->IMFByteStreamHandler_iface.lpVtbl = &stream_handler_vtbl; + handler->IMFAsyncCallback_iface.lpVtbl = &async_callback_vtbl; + handler->refcount = 1; + + hr = IMFByteStreamHandler_QueryInterface(&handler->IMFByteStreamHandler_iface, riid, obj); + IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); + + return hr; +} From 80db1c3ae79a8b0260cd4b76a6d5255cd3858b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:23:38 +0200 Subject: [PATCH 338/758] winegstreamer: Move GStreamerByteStreamHandler to mf_handler.c. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/media_source.c | 2 +- dlls/winegstreamer/mf_handler.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index fde5c231c07..34237da2109 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -149,7 +149,7 @@ HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample, HRESULT wg_transform_read_quartz(struct wg_transform *transform, struct wg_sample *sample); HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj); -HRESULT media_source_create_from_stream(IMFByteStream *stream, IMFMediaSource **out); +HRESULT media_source_create(IMFByteStream *stream, const WCHAR *url, IMFMediaSource **out); HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 78e507347a6..4b62b20c93f 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1441,7 +1441,7 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; -HRESULT media_source_create_from_stream(IMFByteStream *bytestream, IMFMediaSource **out) +HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *uri, IMFMediaSource **out) { BOOL video_selected = FALSE, audio_selected = FALSE; IMFStreamDescriptor **descriptors = NULL; diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index ffb12be38e3..36739269a58 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -168,7 +168,7 @@ static HRESULT stream_handler_create_object(struct handler *handler, WCHAR *url, { HRESULT hr; - if (FAILED(hr = media_source_create_from_stream(stream, (IMFMediaSource **)object))) + if (FAILED(hr = media_source_create(stream, NULL, (IMFMediaSource **)object))) return hr; *type = MF_OBJECT_MEDIASOURCE; From 45a48d4f7f3629142d3ebaeb3938619862ac67cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 15 Apr 2023 20:14:27 +0200 Subject: [PATCH 339/758] winegstreamer: Make IMFAsyncCallback the primary handler interface. CW-Bug-Id: #21953 --- dlls/winegstreamer/mf_handler.c | 61 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index 36739269a58..a8c69b292eb 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -110,8 +110,8 @@ static const IUnknownVtbl async_create_object_vtbl = struct handler { - IMFByteStreamHandler IMFByteStreamHandler_iface; IMFAsyncCallback IMFAsyncCallback_iface; + IMFByteStreamHandler IMFByteStreamHandler_iface; LONG refcount; struct list results; CRITICAL_SECTION cs; @@ -145,13 +145,34 @@ static HRESULT WINAPI async_callback_QueryInterface(IMFAsyncCallback *iface, REF static ULONG WINAPI async_callback_AddRef(IMFAsyncCallback *iface) { struct handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + TRACE("%p, refcount %lu.\n", handler, refcount); + return refcount; } static ULONG WINAPI async_callback_Release(IMFAsyncCallback *iface) { struct handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct result_entry *entry, *next; + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &handler->results, struct result_entry, entry) + { + list_remove(&entry->entry); + IMFAsyncResult_Release(entry->result); + if (entry->object) + IUnknown_Release(entry->object); + free(entry); + } + DeleteCriticalSection(&handler->cs); + free(handler); + } + + return refcount; } static HRESULT WINAPI async_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) @@ -259,34 +280,13 @@ static HRESULT WINAPI stream_handler_QueryInterface(IMFByteStreamHandler *iface, static ULONG WINAPI stream_handler_AddRef(IMFByteStreamHandler *iface) { struct handler *handler = impl_from_IMFByteStreamHandler(iface); - ULONG refcount = InterlockedIncrement(&handler->refcount); - TRACE("%p, refcount %lu.\n", handler, refcount); - return refcount; + return IMFAsyncCallback_AddRef(&handler->IMFAsyncCallback_iface); } static ULONG WINAPI stream_handler_Release(IMFByteStreamHandler *iface) { struct handler *handler = impl_from_IMFByteStreamHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct result_entry *entry, *next; - - TRACE("%p, refcount %lu.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(entry, next, &handler->results, struct result_entry, entry) - { - list_remove(&entry->entry); - IMFAsyncResult_Release(entry->result); - if (entry->object) - IUnknown_Release(entry->object); - free(entry); - } - DeleteCriticalSection(&handler->cs); - free(handler); - } - - return refcount; + return IMFAsyncCallback_Release(&handler->IMFAsyncCallback_iface); } static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, @@ -452,15 +452,14 @@ HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) if (!(handler = calloc(1, sizeof(*handler)))) return E_OUTOFMEMORY; - list_init(&handler->results); - InitializeCriticalSection(&handler->cs); - - handler->IMFByteStreamHandler_iface.lpVtbl = &stream_handler_vtbl; handler->IMFAsyncCallback_iface.lpVtbl = &async_callback_vtbl; + handler->IMFByteStreamHandler_iface.lpVtbl = &stream_handler_vtbl; handler->refcount = 1; + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); hr = IMFByteStreamHandler_QueryInterface(&handler->IMFByteStreamHandler_iface, riid, obj); - IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); + IMFAsyncCallback_Release(&handler->IMFAsyncCallback_iface); return hr; } From 808b780cc637bdfd8cbabcb9f5ecf9394bfafadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:41:24 +0200 Subject: [PATCH 340/758] winegstreamer: Use helpers to manage handler results. CW-Bug-Id: #21953 --- dlls/winegstreamer/mf_handler.c | 342 ++++++++++++++++---------------- 1 file changed, 172 insertions(+), 170 deletions(-) diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index a8c69b292eb..63a6cf6011d 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -41,19 +41,45 @@ struct result_entry IUnknown *object; }; +static HRESULT result_entry_create(IMFAsyncResult *result, MF_OBJECT_TYPE type, + IUnknown *object, struct result_entry **out) +{ + struct result_entry *entry; + + if (!(entry = malloc(sizeof(*entry)))) + return E_OUTOFMEMORY; + + IMFAsyncResult_AddRef((entry->result = result)); + entry->type = type; + if ((entry->object = object)) + IUnknown_AddRef(entry->object); + + *out = entry; + return S_OK; +} + +static void result_entry_destroy(struct result_entry *entry) +{ + IMFAsyncResult_Release(entry->result); + if (entry->object) + IUnknown_Release(entry->object); + free(entry); +} + struct async_create_object { IUnknown IUnknown_iface; LONG refcount; - IPropertyStore *props; IMFByteStream *stream; WCHAR *url; DWORD flags; + IMFAsyncResult *result; }; static struct async_create_object *impl_from_IUnknown(IUnknown *iface) { + if (!iface) return NULL; return CONTAINING_RECORD(iface, struct async_create_object, IUnknown_iface); } @@ -90,8 +116,7 @@ static ULONG WINAPI async_create_object_Release(IUnknown *iface) if (!refcount) { - if (async->props) - IPropertyStore_Release(async->props); + IMFAsyncResult_Release(async->result); if (async->stream) IMFByteStream_Release(async->stream); free(async->url); @@ -108,6 +133,68 @@ static const IUnknownVtbl async_create_object_vtbl = async_create_object_Release, }; +static HRESULT async_create_object_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, + IMFAsyncResult *result, IUnknown **out) +{ + WCHAR *tmp_url = url ? wcsdup(url) : NULL; + struct async_create_object *impl; + + if (!stream && !tmp_url) + return E_INVALIDARG; + if (!(impl = calloc(1, sizeof(*impl)))) + { + free(tmp_url); + return E_OUTOFMEMORY; + } + + impl->IUnknown_iface.lpVtbl = &async_create_object_vtbl; + impl->refcount = 1; + impl->flags = flags; + if ((impl->stream = stream)) + IMFByteStream_AddRef(impl->stream); + impl->url = tmp_url; + IMFAsyncResult_AddRef((impl->result = result)); + + *out = &impl->IUnknown_iface; + return S_OK; +} + +static HRESULT async_create_object_complete(struct async_create_object *async, + struct list *results, CRITICAL_SECTION *results_cs) +{ + IUnknown *object; + HRESULT hr; + + if (async->flags & MF_RESOLUTION_MEDIASOURCE) + hr = media_source_create(async->stream, NULL, (IMFMediaSource **)&object); + else + { + FIXME("Unhandled flags %#lx.\n", async->flags); + hr = E_NOTIMPL; + } + + if (FAILED(hr)) + WARN("Failed to create object, hr %#lx.\n", hr); + else + { + struct result_entry *entry; + + if (FAILED(hr = result_entry_create(async->result, MF_OBJECT_MEDIASOURCE, object, &entry))) + WARN("Failed to add handler result, hr %#lx\n", hr); + else + { + EnterCriticalSection(results_cs); + list_add_tail(results, &entry->entry); + LeaveCriticalSection(results_cs); + } + + IUnknown_Release(object); + } + + IMFAsyncResult_SetStatus(async->result, hr); + return MFInvokeCallback(async->result); +} + struct handler { IMFAsyncCallback IMFAsyncCallback_iface; @@ -117,6 +204,75 @@ struct handler CRITICAL_SECTION cs; }; +static HRESULT handler_begin_create_object(struct handler *handler, DWORD flags, + IMFByteStream *stream, const WCHAR *url, IMFAsyncResult *result) +{ + IUnknown *async; + HRESULT hr; + + if (SUCCEEDED(hr = async_create_object_create(flags, stream, url, result, &async))) + { + if (FAILED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, &handler->IMFAsyncCallback_iface, async))) + WARN("Failed to queue async work item, hr %#lx\n", hr); + IUnknown_Release(async); + } + + return hr; +} + +static struct result_entry *handler_find_result_entry(struct handler *handler, IMFAsyncResult *result) +{ + struct result_entry *entry; + + EnterCriticalSection(&handler->cs); + LIST_FOR_EACH_ENTRY(entry, &handler->results, struct result_entry, entry) + { + if (result == entry->result) + { + list_remove(&entry->entry); + LeaveCriticalSection(&handler->cs); + return entry; + } + } + LeaveCriticalSection(&handler->cs); + + return NULL; +} + +static HRESULT handler_end_create_object(struct handler *handler, + IMFAsyncResult *result, MF_OBJECT_TYPE *type, IUnknown **object) +{ + struct result_entry *entry; + HRESULT hr; + + if (!(entry = handler_find_result_entry(handler, result))) + { + *type = MF_OBJECT_INVALID; + *object = NULL; + return MF_E_UNEXPECTED; + } + + hr = IMFAsyncResult_GetStatus(entry->result); + *type = entry->type; + *object = entry->object; + entry->object = NULL; + + result_entry_destroy(entry); + return hr; +} + +static HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cookie) +{ + IMFAsyncResult *result = (IMFAsyncResult *)cookie; + struct result_entry *entry; + + if (!(entry = handler_find_result_entry(handler, result))) + return MF_E_UNEXPECTED; + + result_entry_destroy(entry); + return S_OK; +} + static struct handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) { return CONTAINING_RECORD(iface, struct handler, IMFAsyncCallback_iface); @@ -161,13 +317,7 @@ static ULONG WINAPI async_callback_Release(IMFAsyncCallback *iface) if (!refcount) { LIST_FOR_EACH_ENTRY_SAFE(entry, next, &handler->results, struct result_entry, entry) - { - list_remove(&entry->entry); - IMFAsyncResult_Release(entry->result); - if (entry->object) - IUnknown_Release(entry->object); - free(entry); - } + result_entry_destroy(entry); DeleteCriticalSection(&handler->cs); free(handler); } @@ -180,75 +330,20 @@ static HRESULT WINAPI async_callback_GetParameters(IMFAsyncCallback *iface, DWOR return E_NOTIMPL; } -static HRESULT stream_handler_create_object(struct handler *handler, WCHAR *url, - IMFByteStream *stream, DWORD flags, IPropertyStore *props, IUnknown **object, MF_OBJECT_TYPE *type) -{ - TRACE("%p, %s, %p, %#lx, %p, %p, %p.\n", handler, debugstr_w(url), stream, flags, props, object, type); - - if (flags & MF_RESOLUTION_MEDIASOURCE) - { - HRESULT hr; - - if (FAILED(hr = media_source_create(stream, NULL, (IMFMediaSource **)object))) - return hr; - - *type = MF_OBJECT_MEDIASOURCE; - return S_OK; - } - else - { - FIXME("Unhandled flags %#lx.\n", flags); - return E_NOTIMPL; - } -} - static HRESULT WINAPI async_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct handler *handler = impl_from_IMFAsyncCallback(iface); - MF_OBJECT_TYPE type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; struct async_create_object *async; - struct result_entry *entry; - IMFAsyncResult *caller; - HRESULT hr; - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + TRACE("iface %p, result %p\n", iface, result); - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + if (!(async = impl_from_IUnknown(IMFAsyncResult_GetStateNoAddRef(result)))) { WARN("Expected context set for callee result.\n"); - return hr; - } - - async = impl_from_IUnknown(context_object); - - hr = stream_handler_create_object(handler, async->url, async->stream, async->flags, - async->props, &object, &type); - - if ((entry = malloc(sizeof(*entry)))) - { - entry->result = caller; - IMFAsyncResult_AddRef(entry->result); - entry->type = type; - entry->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &entry->entry); - LeaveCriticalSection(&handler->cs); + return E_FAIL; } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IUnknown_Release(&async->IUnknown_iface); - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - return S_OK; + return async_create_object_complete(async, &handler->results, &handler->cs); } static const IMFAsyncCallbackVtbl async_callback_vtbl = @@ -294,8 +389,7 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa IUnknown **cookie, IMFAsyncCallback *callback, IUnknown *state) { struct handler *handler = impl_from_IMFByteStreamHandler(iface); - struct async_create_object *async; - IMFAsyncResult *caller, *item; + IMFAsyncResult *result; HRESULT hr; TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cookie, callback, state); @@ -303,50 +397,16 @@ static HRESULT WINAPI stream_handler_BeginCreateObject(IMFByteStreamHandler *ifa if (cookie) *cookie = NULL; - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + if (FAILED(hr = MFCreateAsyncResult((IUnknown *)iface, callback, state, &result))) return hr; - if (!(async = calloc(1, sizeof(*async)))) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - async->IUnknown_iface.lpVtbl = &async_create_object_vtbl; - async->refcount = 1; - async->props = props; - if (async->props) - IPropertyStore_AddRef(async->props); - async->flags = flags; - async->stream = stream; - if (async->stream) - IMFByteStream_AddRef(async->stream); - if (url) - async->url = wcsdup(url); - if (!async->stream) + if (SUCCEEDED(hr = handler_begin_create_object(handler, flags, stream, url, result)) && cookie) { - IMFAsyncResult_Release(caller); - IUnknown_Release(&async->IUnknown_iface); - return E_OUTOFMEMORY; + *cookie = (IUnknown *)result; + IUnknown_AddRef(*cookie); } - hr = MFCreateAsyncResult(&async->IUnknown_iface, &handler->IMFAsyncCallback_iface, - (IUnknown *)caller, &item); - IUnknown_Release(&async->IUnknown_iface); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cookie) - { - *cookie = (IUnknown *)caller; - IUnknown_AddRef(*cookie); - } - } - - IMFAsyncResult_Release(item); - } - IMFAsyncResult_Release(caller); + IMFAsyncResult_Release(result); return hr; } @@ -355,73 +415,15 @@ static HRESULT WINAPI stream_handler_EndCreateObject(IMFByteStreamHandler *iface IMFAsyncResult *result, MF_OBJECT_TYPE *type, IUnknown **object) { struct handler *handler = impl_from_IMFByteStreamHandler(iface); - struct result_entry *found = NULL, *entry; - HRESULT hr; - TRACE("%p, %p, %p, %p.\n", iface, result, type, object); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(entry, &handler->results, struct result_entry, entry) - { - if (result == entry->result) - { - list_remove(&entry->entry); - found = entry; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - *type = found->type; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - free(found); - } - else - { - *type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; + return handler_end_create_object(handler, result, type, object); } static HRESULT WINAPI stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cookie) { struct handler *handler = impl_from_IMFByteStreamHandler(iface); - struct result_entry *found = NULL, *entry; - TRACE("%p, %p.\n", iface, cookie); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(entry, &handler->results, struct result_entry, entry) - { - if (cookie == (IUnknown *)entry->result) - { - list_remove(&entry->entry); - found = entry; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; + return handler_cancel_object_creation(handler, cookie); } static HRESULT WINAPI stream_handler_GetMaxNumberOfBytesRequiredForResolution( From 1c4ad763ac57c7dc8fd32f08c446e17b3d0096ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:42:23 +0200 Subject: [PATCH 341/758] winegstreamer: Implement a scheme handler for the media source. Link: https://github.com/ValveSoftware/wine/pull/142 CW-Bug-Id: #20485 CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/media_source.c | 20 ++++ dlls/winegstreamer/mf_handler.c | 115 ++++++++++++++++++- dlls/winegstreamer/mfplat.c | 2 + dlls/winegstreamer/winegstreamer.rgs | 28 +++++ dlls/winegstreamer/winegstreamer_classes.idl | 6 + 6 files changed, 172 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 34237da2109..0cd2dbc0825 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -149,7 +149,9 @@ HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample, HRESULT wg_transform_read_quartz(struct wg_transform *transform, struct wg_sample *sample); HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj); +HRESULT winegstreamer_scheme_handler_create(REFIID riid, void **obj); HRESULT media_source_create(IMFByteStream *stream, const WCHAR *url, IMFMediaSource **out); +HRESULT media_source_create_from_url(const WCHAR *url, IMFMediaSource **out); HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 4b62b20c93f..0a307a9a6d2 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1646,3 +1646,23 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *uri, IMFMedi free(object); return hr; } + +HRESULT media_source_create_from_url(const WCHAR *url, IMFMediaSource **out) +{ + IMFByteStream *bytestream; + IStream *stream; + HRESULT hr; + + if (FAILED(hr = CreateStreamOnHGlobal(0, TRUE, &stream))) + return hr; + + hr = MFCreateMFByteStreamOnStream(stream, &bytestream); + IStream_Release(stream); + if (FAILED(hr)) + return hr; + + hr = media_source_create(bytestream, url, out); + IMFByteStream_Release(bytestream); + + return hr; +} diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index 63a6cf6011d..e030854ccd5 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -166,7 +166,12 @@ static HRESULT async_create_object_complete(struct async_create_object *async, HRESULT hr; if (async->flags & MF_RESOLUTION_MEDIASOURCE) - hr = media_source_create(async->stream, NULL, (IMFMediaSource **)&object); + { + if (!async->stream) + hr = media_source_create_from_url(async->url, (IMFMediaSource **)&object); + else + hr = media_source_create(async->stream, NULL, (IMFMediaSource **)&object); + } else { FIXME("Unhandled flags %#lx.\n", async->flags); @@ -199,6 +204,7 @@ struct handler { IMFAsyncCallback IMFAsyncCallback_iface; IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFSchemeHandler IMFSchemeHandler_iface; LONG refcount; struct list results; CRITICAL_SECTION cs; @@ -283,6 +289,11 @@ static struct handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *ifac return CONTAINING_RECORD(iface, struct handler, IMFByteStreamHandler_iface); } +static struct handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct handler, IMFSchemeHandler_iface); +} + static HRESULT WINAPI async_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IMFAsyncCallback) @@ -444,6 +455,86 @@ static const IMFByteStreamHandlerVtbl stream_handler_vtbl = stream_handler_GetMaxNumberOfBytesRequiredForResolution, }; +static HRESULT WINAPI scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFSchemeHandler) + || IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFSchemeHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI scheme_handler_AddRef(IMFSchemeHandler *iface) +{ + struct handler *handler = impl_from_IMFSchemeHandler(iface); + return IMFAsyncCallback_AddRef(&handler->IMFAsyncCallback_iface); +} + +static ULONG WINAPI scheme_handler_Release(IMFSchemeHandler *iface) +{ + struct handler *handler = impl_from_IMFSchemeHandler(iface); + return IMFAsyncCallback_Release(&handler->IMFAsyncCallback_iface); +} + +static HRESULT WINAPI scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, + DWORD flags, IPropertyStore *props, IUnknown **cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct handler *handler = impl_from_IMFSchemeHandler(iface); + IMFAsyncResult *result; + HRESULT hr; + + TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cookie, callback, state); + + if (cookie) + *cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult((IUnknown *)iface, callback, state, &result))) + return hr; + + if (SUCCEEDED(hr = handler_begin_create_object(handler, flags, NULL, url, result)) && cookie) + { + *cookie = (IUnknown *)result; + IUnknown_AddRef(*cookie); + } + + IMFAsyncResult_Release(result); + + return hr; +} + +static HRESULT WINAPI scheme_handler_EndCreateObject(IMFSchemeHandler *iface, + IMFAsyncResult *result, MF_OBJECT_TYPE *type, IUnknown **object) +{ + struct handler *handler = impl_from_IMFSchemeHandler(iface); + TRACE("%p, %p, %p, %p.\n", iface, result, type, object); + return handler_end_create_object(handler, result, type, object); +} + +static HRESULT WINAPI scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cookie) +{ + struct handler *handler = impl_from_IMFSchemeHandler(iface); + TRACE("%p, %p.\n", iface, cookie); + return handler_cancel_object_creation(handler, cookie); +} + +static const IMFSchemeHandlerVtbl scheme_handler_vtbl = +{ + scheme_handler_QueryInterface, + scheme_handler_AddRef, + scheme_handler_Release, + scheme_handler_BeginCreateObject, + scheme_handler_EndCreateObject, + scheme_handler_CancelObjectCreation, +}; + HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) { struct handler *handler; @@ -465,3 +556,25 @@ HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) return hr; } + +HRESULT winegstreamer_scheme_handler_create(REFIID riid, void **obj) +{ + struct handler *handler; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(handler = calloc(1, sizeof(*handler)))) + return E_OUTOFMEMORY; + + handler->IMFAsyncCallback_iface.lpVtbl = &async_callback_vtbl; + handler->IMFSchemeHandler_iface.lpVtbl = &scheme_handler_vtbl; + handler->refcount = 1; + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); + + hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); + IMFAsyncCallback_Release(&handler->IMFAsyncCallback_iface); + + return hr; +} diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index ea5ea33ee9d..27ec2aaea86 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -121,6 +121,7 @@ static const IClassFactoryVtbl class_factory_vtbl = }; static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; +static const GUID CLSID_GStreamerSchemeHandler = {0x587eeb6a,0x7336,0x4ebd,{0xa4,0xf2,0x91,0xc9,0x48,0xde,0x62,0x2c}}; static const struct class_object { @@ -131,6 +132,7 @@ class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, { &CLSID_GStreamerByteStreamHandler, &winegstreamer_stream_handler_create }, + { &CLSID_GStreamerSchemeHandler, &winegstreamer_scheme_handler_create }, { &CLSID_MSAACDecMFT, &aac_decoder_create }, { &CLSID_MSH264DecoderMFT, &h264_decoder_create }, }; diff --git a/dlls/winegstreamer/winegstreamer.rgs b/dlls/winegstreamer/winegstreamer.rgs index 923ba673f8c..c50d3a05747 100644 --- a/dlls/winegstreamer/winegstreamer.rgs +++ b/dlls/winegstreamer/winegstreamer.rgs @@ -12,3 +12,31 @@ HKCR } } } + +HKLM +{ + NoRemove 'Software' + { + NoRemove 'Microsoft' + { + NoRemove 'Windows Media Foundation' + { + NoRemove 'SchemeHandlers' + { + 'http:' + { + val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler' + } + 'https:' + { + val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler' + } + 'rtsp:' + { + val '{587eeb6a-7336-4ebd-a4f2-91c948de622c}' = s 'GStreamer Scheme Handler' + } + } + } + } + } +} diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 30a99c9acfb..13b8a044695 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -111,3 +111,9 @@ coclass CResamplerMediaObject {} uuid(98230571-0087-4204-b020-3282538e57d3) ] coclass CColorConvertDMO {} + +[ + threading(both), + uuid(587eeb6a-7336-4ebd-a4f2-91c948de622c) +] +coclass GStreamerSchemeHandler {} From d90c9af07b2a8dd5af89976b8832aa75fa17daab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 17:49:08 +0200 Subject: [PATCH 342/758] mfplat: Use the io queue for bytestream requests. CW-Bug-Id: #21953 --- dlls/mfplat/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index ed69d501f2a..d8f621d4559 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -3826,7 +3826,7 @@ static HRESULT bytestream_create_io_request(struct bytestream *stream, enum asyn &stream->write_callback, NULL, &request))) goto failed; - RtwqPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, 0, request); + RtwqPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, 0, request); IRtwqAsyncResult_Release(request); failed: From 345f6a819ae7f9445080e6cc01b6c92138c21a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 19:16:18 +0200 Subject: [PATCH 343/758] winegstreamer: Call BeginRead / EndRead to read the bytestream header. CW-Bug-Id: #21953 --- dlls/winegstreamer/mf_handler.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index e030854ccd5..3d7f36e749c 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -75,8 +75,13 @@ struct async_create_object WCHAR *url; DWORD flags; IMFAsyncResult *result; + + UINT64 size; + BYTE buffer[]; }; +C_ASSERT(sizeof(struct async_create_object) == offsetof(struct async_create_object, buffer[0])); + static struct async_create_object *impl_from_IUnknown(IUnknown *iface) { if (!iface) return NULL; @@ -134,14 +139,14 @@ static const IUnknownVtbl async_create_object_vtbl = }; static HRESULT async_create_object_create(DWORD flags, IMFByteStream *stream, const WCHAR *url, - IMFAsyncResult *result, IUnknown **out) + IMFAsyncResult *result, UINT size, IUnknown **out, BYTE **buffer) { WCHAR *tmp_url = url ? wcsdup(url) : NULL; struct async_create_object *impl; if (!stream && !tmp_url) return E_INVALIDARG; - if (!(impl = calloc(1, sizeof(*impl)))) + if (!(impl = calloc(1, offsetof(struct async_create_object, buffer[size])))) { free(tmp_url); return E_OUTOFMEMORY; @@ -155,6 +160,7 @@ static HRESULT async_create_object_create(DWORD flags, IMFByteStream *stream, co impl->url = tmp_url; IMFAsyncResult_AddRef((impl->result = result)); + *buffer = impl->buffer; *out = &impl->IUnknown_iface; return S_OK; } @@ -213,12 +219,16 @@ struct handler static HRESULT handler_begin_create_object(struct handler *handler, DWORD flags, IMFByteStream *stream, const WCHAR *url, IMFAsyncResult *result) { + UINT size = 0x2000; IUnknown *async; HRESULT hr; + BYTE *buffer; - if (SUCCEEDED(hr = async_create_object_create(flags, stream, url, result, &async))) + if (SUCCEEDED(hr = async_create_object_create(flags, stream, url, result, size, &async, &buffer))) { - if (FAILED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, &handler->IMFAsyncCallback_iface, async))) + if (stream && FAILED(hr = IMFByteStream_BeginRead(stream, buffer, size, &handler->IMFAsyncCallback_iface, async))) + WARN("Failed to begin reading from stream, hr %#lx\n", hr); + if (!stream && FAILED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, &handler->IMFAsyncCallback_iface, async))) WARN("Failed to queue async work item, hr %#lx\n", hr); IUnknown_Release(async); } @@ -338,13 +348,17 @@ static ULONG WINAPI async_callback_Release(IMFAsyncCallback *iface) static HRESULT WINAPI async_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) { - return E_NOTIMPL; + *flags = 0; + *queue = MFASYNC_CALLBACK_QUEUE_IO; + return S_OK; } static HRESULT WINAPI async_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct handler *handler = impl_from_IMFAsyncCallback(iface); struct async_create_object *async; + ULONG size = 0; + HRESULT hr; TRACE("iface %p, result %p\n", iface, result); @@ -354,6 +368,10 @@ static HRESULT WINAPI async_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncRes return E_FAIL; } + if (async->stream && FAILED(hr = IMFByteStream_EndRead(async->stream, result, &size))) + WARN("Failed to complete stream read, hr %#lx\n", hr); + async->size = size; + return async_create_object_complete(async, &handler->results, &handler->cs); } From f4bc66e25aa3ae1d8eded9eab7fb5d016a22394f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:51:25 +0200 Subject: [PATCH 344/758] winegstreamer: Guard new media source implementation with WINE_NEW_MEDIA_SOURCE. CW-Bug-Id: #21953 --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 3 +- dlls/winegstreamer/media_source.c | 26 +- dlls/winegstreamer/media_source_old.c | 1668 +++++++++++++++++++++++++ dlls/winegstreamer/mf_handler.c | 10 +- 5 files changed, 1682 insertions(+), 26 deletions(-) create mode 100644 dlls/winegstreamer/media_source_old.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 49b4dd2c631..56afcea3189 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ h264_decoder.c \ main.c \ media_source.c \ + media_source_old.c \ mf_handler.c \ mfplat.c \ quartz_parser.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 0cd2dbc0825..bb5db1c5f51 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -150,7 +150,8 @@ HRESULT wg_transform_read_quartz(struct wg_transform *transform, struct wg_sampl HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj); HRESULT winegstreamer_scheme_handler_create(REFIID riid, void **obj); -HRESULT media_source_create(IMFByteStream *stream, const WCHAR *url, IMFMediaSource **out); +HRESULT media_source_create(IMFByteStream *stream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out); +HRESULT media_source_create_old(IMFByteStream *stream, const WCHAR *url, IMFMediaSource **out); HRESULT media_source_create_from_url(const WCHAR *url, IMFMediaSource **out); HRESULT aac_decoder_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 0a307a9a6d2..ec1223c7bfd 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1441,7 +1441,7 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; -HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *uri, IMFMediaSource **out) +HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { BOOL video_selected = FALSE, audio_selected = FALSE; IMFStreamDescriptor **descriptors = NULL; @@ -1490,7 +1490,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *uri, IMFMedi if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) goto fail; - if (!(parser = wg_parser_create(uri ? WG_PARSER_URIDECODEBIN : WG_PARSER_DECODEBIN, false))) + if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, false))) { hr = E_OUTOFMEMORY; goto fail; @@ -1501,7 +1501,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *uri, IMFMedi object->state = SOURCE_OPENING; - if (FAILED(hr = wg_parser_connect(parser, file_size, uri))) + if (FAILED(hr = wg_parser_connect(parser, file_size, NULL))) goto fail; stream_count = wg_parser_get_stream_count(parser); @@ -1646,23 +1646,3 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *uri, IMFMedi free(object); return hr; } - -HRESULT media_source_create_from_url(const WCHAR *url, IMFMediaSource **out) -{ - IMFByteStream *bytestream; - IStream *stream; - HRESULT hr; - - if (FAILED(hr = CreateStreamOnHGlobal(0, TRUE, &stream))) - return hr; - - hr = MFCreateMFByteStreamOnStream(stream, &bytestream); - IStream_Release(stream); - if (FAILED(hr)) - return hr; - - hr = media_source_create(bytestream, url, out); - IMFByteStream_Release(bytestream); - - return hr; -} diff --git a/dlls/winegstreamer/media_source_old.c b/dlls/winegstreamer/media_source_old.c new file mode 100644 index 00000000000..a2fe13e4da8 --- /dev/null +++ b/dlls/winegstreamer/media_source_old.c @@ -0,0 +1,1668 @@ +/* GStreamer Media Source + * + * Copyright 2020 Derek Lesho + * Copyright 2020 Zebediah Figura 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 "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +extern const GUID MFVideoFormat_ABGR32; + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + + IMFMediaSource *media_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + + struct wg_parser_stream *wg_stream; + + IUnknown **token_queue; + LONG token_queue_count; + LONG token_queue_cap; + + DWORD stream_id; + BOOL active; + BOOL eos; +}; + +enum source_async_op +{ + SOURCE_ASYNC_START, + SOURCE_ASYNC_PAUSE, + SOURCE_ASYNC_STOP, + SOURCE_ASYNC_REQUEST_SAMPLE, +}; + +struct source_async_command +{ + IUnknown IUnknown_iface; + LONG refcount; + enum source_async_op op; + union + { + struct + { + IMFPresentationDescriptor *descriptor; + GUID format; + PROPVARIANT position; + } start; + struct + { + struct media_stream *stream; + IUnknown *token; + } request_sample; + } u; +}; + +struct media_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFGetService IMFGetService_iface; + IMFRateSupport IMFRateSupport_iface; + IMFRateControl IMFRateControl_iface; + IMFAsyncCallback async_commands_callback; + LONG ref; + DWORD async_commands_queue; + IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + + CRITICAL_SECTION cs; + + struct wg_parser *wg_parser; + + struct media_stream **streams; + ULONG stream_count; + IMFPresentationDescriptor *pres_desc; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; + float rate; + + HANDLE read_thread; + bool read_thread_shutdown; +}; + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); +} + +static inline struct media_source *impl_from_IMFGetService(IMFGetService *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFGetService_iface); +} + +static inline struct media_source *impl_from_IMFRateSupport(IMFRateSupport *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFRateSupport_iface); +} + +static inline struct media_source *impl_from_IMFRateControl(IMFRateControl *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFRateControl_iface); +} + +static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, async_commands_callback); +} + +static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface); +} + +static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI source_async_command_AddRef(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + return InterlockedIncrement(&command->refcount); +} + +static ULONG WINAPI source_async_command_Release(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&command->refcount); + + if (!refcount) + { + if (command->op == SOURCE_ASYNC_START) + PropVariantClear(&command->u.start.position); + else if (command->op == SOURCE_ASYNC_REQUEST_SAMPLE) + { + if (command->u.request_sample.token) + IUnknown_Release(command->u.request_sample.token); + } + free(command); + } + + return refcount; +} + +static const IUnknownVtbl source_async_command_vtbl = +{ + source_async_command_QueryInterface, + source_async_command_AddRef, + source_async_command_Release, +}; + +static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret) +{ + struct source_async_command *command; + + if (!(command = calloc(1, sizeof(*command)))) + return E_OUTOFMEMORY; + + command->IUnknown_iface.lpVtbl = &source_async_command_vtbl; + command->op = op; + + *ret = command; + + return S_OK; +} + +static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected) +{ + ULONG sd_count; + IMFStreamDescriptor *ret; + unsigned int i; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count))) + return NULL; + + for (i = 0; i < sd_count; i++) + { + DWORD stream_id; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret))) + return NULL; + + if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id) + return ret; + + IMFStreamDescriptor_Release(ret); + } + return NULL; +} + +static BOOL enqueue_token(struct media_stream *stream, IUnknown *token) +{ + if (stream->token_queue_count == stream->token_queue_cap) + { + IUnknown **buf; + stream->token_queue_cap = stream->token_queue_cap * 2 + 1; + buf = realloc(stream->token_queue, stream->token_queue_cap * sizeof(*buf)); + if (buf) + stream->token_queue = buf; + else + { + stream->token_queue_cap = stream->token_queue_count; + return FALSE; + } + } + stream->token_queue[stream->token_queue_count++] = token; + return TRUE; +} + +static void flush_token_queue(struct media_stream *stream, BOOL send) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + LONG i; + + for (i = 0; i < stream->token_queue_count; i++) + { + if (send) + { + HRESULT hr; + struct source_async_command *command; + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + { + command->u.request_sample.stream = stream; + command->u.request_sample.token = stream->token_queue[i]; + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, + &command->IUnknown_iface); + } + if (FAILED(hr)) + WARN("Could not enqueue sample request, hr %#lx\n", hr); + } + else if (stream->token_queue[i]) + IUnknown_Release(stream->token_queue[i]); + } + free(stream->token_queue); + stream->token_queue = NULL; + stream->token_queue_count = 0; + stream->token_queue_cap = 0; +} + +static void start_pipeline(struct media_source *source, struct source_async_command *command) +{ + PROPVARIANT *position = &command->u.start.position; + BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY; + unsigned int i; + + /* seek to beginning on stop->play */ + if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) + { + position->vt = VT_I8; + position->hVal.QuadPart = 0; + } + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream; + IMFStreamDescriptor *sd; + IMFMediaTypeHandler *mth; + IMFMediaType *current_mt; + DWORD stream_id; + BOOL was_active; + BOOL selected; + + stream = source->streams[i]; + + IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); + + sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected); + IMFStreamDescriptor_Release(sd); + + was_active = stream->active; + stream->active = selected; + + if (selected) + { + struct wg_format format; + + IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth); + IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt); + + mf_media_type_to_wg_format(current_mt, &format); + wg_parser_stream_enable(stream->wg_stream, &format, 0); + + IMFMediaType_Release(current_mt); + IMFMediaTypeHandler_Release(mth); + } + else + { + wg_parser_stream_disable(stream->wg_stream); + } + + if (position->vt != VT_EMPTY) + stream->eos = FALSE; + + if (selected) + { + TRACE("Stream %u (%p) selected\n", i, stream); + IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &stream->IMFMediaStream_iface); + + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, + seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position); + } + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + seek_message ? MESourceSeeked : MESourceStarted, + &GUID_NULL, S_OK, position); + + source->state = SOURCE_RUNNING; + + if (position->vt == VT_I8) + wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0, position->hVal.QuadPart, 0, + AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning); + + for (i = 0; i < source->stream_count; i++) + flush_token_queue(source->streams[i], position->vt == VT_EMPTY); +} + +static void pause_pipeline(struct media_source *source) +{ + unsigned int i; + HRESULT hr; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamPaused, + &GUID_NULL, S_OK, NULL))) + WARN("Failed to queue MEStreamPaused event, hr %#lx\n", hr); + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourcePaused, &GUID_NULL, S_OK, NULL); + + source->state = SOURCE_PAUSED; +} + +static void stop_pipeline(struct media_source *source) +{ + unsigned int i; + HRESULT hr; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamStopped, + &GUID_NULL, S_OK, NULL))) + WARN("Failed to queue MEStreamStopped event, hr %#lx\n", hr); + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); + + source->state = SOURCE_STOPPED; + + for (i = 0; i < source->stream_count; i++) + flush_token_queue(source->streams[i], FALSE); +} + +static void dispatch_end_of_presentation(struct media_source *source) +{ + PROPVARIANT empty = {.vt = VT_EMPTY}; + unsigned int i; + + /* A stream has ended, check whether all have */ + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && !stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + +static void send_buffer(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) +{ + IMFMediaBuffer *buffer; + IMFSample *sample; + HRESULT hr; + BYTE *data; + + if (FAILED(hr = MFCreateSample(&sample))) + { + ERR("Failed to create sample, hr %#lx.\n", hr); + return; + } + + if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer->size, &buffer))) + { + ERR("Failed to create buffer, hr %#lx.\n", hr); + IMFSample_Release(sample); + return; + } + + if (FAILED(hr = IMFSample_AddBuffer(sample, buffer))) + { + ERR("Failed to add buffer, hr %#lx.\n", hr); + goto out; + } + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer->size))) + { + ERR("Failed to set size, hr %#lx.\n", hr); + goto out; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) + { + ERR("Failed to lock buffer, hr %#lx.\n", hr); + goto out; + } + + if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) + { + wg_parser_stream_release_buffer(stream->wg_stream); + IMFMediaBuffer_Unlock(buffer); + goto out; + } + wg_parser_stream_release_buffer(stream->wg_stream); + + if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) + { + ERR("Failed to unlock buffer, hr %#lx.\n", hr); + goto out; + } + + if (FAILED(hr = IMFSample_SetSampleTime(sample, wg_buffer->pts))) + { + ERR("Failed to set sample time, hr %#lx.\n", hr); + goto out; + } + + if (FAILED(hr = IMFSample_SetSampleDuration(sample, wg_buffer->duration))) + { + ERR("Failed to set sample duration, hr %#lx.\n", hr); + goto out; + } + + if (token) + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + + IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, + &GUID_NULL, S_OK, (IUnknown *)sample); + +out: + IMFMediaBuffer_Release(buffer); + IMFSample_Release(sample); +} + +static void wait_on_sample(struct media_stream *stream, IUnknown *token) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + PROPVARIANT empty_var = {.vt = VT_EMPTY}; + struct wg_parser_buffer buffer; + + TRACE("%p, %p\n", stream, token); + + if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) + { + send_buffer(stream, &buffer, token); + } + else + { + stream->eos = TRUE; + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); + dispatch_end_of_presentation(source); + } +} + +static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + struct source_async_command *command; + IUnknown *state; + HRESULT hr; + + if (FAILED(hr = IMFAsyncResult_GetState(result, &state))) + return hr; + + EnterCriticalSection(&source->cs); + + command = impl_from_async_command_IUnknown(state); + switch (command->op) + { + case SOURCE_ASYNC_START: + if (source->state != SOURCE_SHUTDOWN) + start_pipeline(source, command); + break; + case SOURCE_ASYNC_PAUSE: + if (source->state != SOURCE_SHUTDOWN) + pause_pipeline(source); + break; + case SOURCE_ASYNC_STOP: + if (source->state != SOURCE_SHUTDOWN) + stop_pipeline(source); + break; + case SOURCE_ASYNC_REQUEST_SAMPLE: + if (source->state == SOURCE_PAUSED) + enqueue_token(command->u.request_sample.stream, command->u.request_sample.token); + else if (source->state == SOURCE_RUNNING) + wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token); + break; + } + + LeaveCriticalSection(&source->cs); + + IUnknown_Release(state); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl = +{ + callback_QueryInterface, + source_async_commands_callback_AddRef, + source_async_commands_callback_Release, + callback_GetParameters, + source_async_commands_Invoke, +}; + +static DWORD CALLBACK read_thread(void *arg) +{ + struct media_source *source = arg; + IMFByteStream *byte_stream = source->byte_stream; + size_t buffer_size = 4096; + uint64_t file_size; + void *data; + + if (!(data = malloc(buffer_size))) + return 0; + + IMFByteStream_GetLength(byte_stream, &file_size); + + TRACE("Starting read thread for media source %p.\n", source); + + while (!source->read_thread_shutdown) + { + uint64_t offset; + ULONG ret_size; + uint32_t size; + HRESULT hr; + + if (!wg_parser_get_next_read_offset(source->wg_parser, &offset, &size)) + continue; + + if (offset >= file_size) + size = 0; + else if (offset + size >= file_size) + size = file_size - offset; + + /* Some IMFByteStreams (including the standard file-based stream) return + * an error when reading past the file size. */ + if (!size) + { + wg_parser_push_data(source->wg_parser, data, 0); + continue; + } + + if (!array_reserve(&data, &buffer_size, size, 1)) + { + free(data); + return 0; + } + + ret_size = 0; + + if (SUCCEEDED(hr = IMFByteStream_SetCurrentPosition(byte_stream, offset))) + hr = IMFByteStream_Read(byte_stream, data, size, &ret_size); + if (FAILED(hr)) + ERR("Failed to read %u bytes at offset %I64u, hr %#lx.\n", size, offset, hr); + else if (ret_size != size) + ERR("Unexpected short read: requested %u bytes, got %lu.\n", size, ret_size); + wg_parser_push_data(source->wg_parser, SUCCEEDED(hr) ? data : NULL, ret_size); + } + + free(data); + TRACE("Media source is shutting down; exiting.\n"); + return 0; +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &stream->IMFMediaStream_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&stream->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedDecrement(&stream->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + if (!ref) + { + IMFMediaSource_Release(stream->media_source); + IMFStreamDescriptor_Release(stream->descriptor); + IMFMediaEventQueue_Release(stream->event_queue); + flush_token_queue(stream, FALSE); + free(stream); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p, %p.\n", stream, result, event); + + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +} + +static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **out) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, out); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + { + IMFMediaSource_AddRef(&source->IMFMediaSource_iface); + *out = &source->IMFMediaSource_iface; + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, descriptor); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + { + IMFStreamDescriptor_AddRef(stream->descriptor); + *descriptor = stream->descriptor; + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p, %p.\n", iface, token); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (!stream->active) + hr = MF_E_MEDIA_SOURCE_WRONGSTATE; + else if (stream->eos) + hr = MF_E_END_OF_STREAM; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + { + command->u.request_sample.stream = stream; + if (token) + IUnknown_AddRef(token); + command->u.request_sample.token = token; + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static const IMFMediaStreamVtbl media_stream_vtbl = +{ + media_stream_QueryInterface, + media_stream_AddRef, + media_stream_Release, + media_stream_GetEvent, + media_stream_BeginGetEvent, + media_stream_EndGetEvent, + media_stream_QueueEvent, + media_stream_GetMediaSource, + media_stream_GetStreamDescriptor, + media_stream_RequestSample +}; + +static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, + struct media_stream **out) +{ + struct wg_parser *wg_parser = impl_from_IMFMediaSource(source)->wg_parser; + struct media_stream *object; + HRESULT hr; + + TRACE("source %p, id %lu.\n", source, id); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + object->ref = 1; + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + { + free(object); + return hr; + } + + IMFMediaSource_AddRef(source); + object->media_source = source; + object->stream_id = id; + + object->active = FALSE; + object->eos = FALSE; + object->wg_stream = wg_parser_get_stream(wg_parser, id); + + TRACE("Created stream object %p.\n", object); + + *out = object; + return S_OK; +} + +static HRESULT media_stream_init_desc(struct media_stream *stream) +{ + IMFMediaTypeHandler *type_handler = NULL; + IMFMediaType *stream_types[9]; + struct wg_format format; + DWORD type_count = 0; + HRESULT hr = S_OK; + unsigned int i; + + wg_parser_stream_get_preferred_format(stream->wg_stream, &format); + + if (format.major_type == WG_MAJOR_TYPE_VIDEO) + { + /* These are the most common native output types of decoders: + https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */ + static const GUID *const video_types[] = + { + &MFVideoFormat_NV12, + &MFVideoFormat_YV12, + &MFVideoFormat_YUY2, + &MFVideoFormat_IYUV, + &MFVideoFormat_I420, + &MFVideoFormat_ARGB32, + &MFVideoFormat_RGB32, + &MFVideoFormat_ABGR32, + }; + + IMFMediaType *base_type = mf_media_type_from_wg_format(&format); + GUID base_subtype; + + if (!base_type) + { + hr = MF_E_INVALIDMEDIATYPE; + goto done; + } + + IMFMediaType_SetUINT32(base_type, &MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Normal); + + IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype); + + stream_types[0] = base_type; + type_count = 1; + + for (i = 0; i < ARRAY_SIZE(video_types); i++) + { + IMFMediaType *new_type; + + if (IsEqualGUID(&base_subtype, video_types[i])) + continue; + + if (FAILED(hr = MFCreateMediaType(&new_type))) + goto done; + stream_types[type_count++] = new_type; + + if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i]))) + goto done; + } + } + else if (format.major_type == WG_MAJOR_TYPE_AUDIO) + { + /* Expose at least one PCM and one floating point type for the + consumer to pick from. Moreover, ensure that we expose S16LE first, + as games such as MGSV expect the native media type to be 16 bps. */ + static const enum wg_audio_format audio_types[] = + { + WG_AUDIO_FORMAT_S16LE, + WG_AUDIO_FORMAT_F32LE, + }; + + BOOL has_native_format = FALSE; + + for (i = 0; i < ARRAY_SIZE(audio_types); i++) + { + struct wg_format new_format; + + new_format = format; + new_format.u.audio.format = audio_types[i]; + if ((stream_types[type_count] = mf_media_type_from_wg_format(&new_format))) + { + if (format.u.audio.format == audio_types[i]) + has_native_format = TRUE; + type_count++; + } + } + + if (!has_native_format && (stream_types[type_count] = mf_media_type_from_wg_format(&format))) + type_count++; + } + else + { + if ((stream_types[0] = mf_media_type_from_wg_format(&format))) + type_count = 1; + } + + assert(type_count <= ARRAY_SIZE(stream_types)); + + if (!type_count) + { + ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n"); + return E_FAIL; + } + + if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor))) + goto done; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) + { + IMFStreamDescriptor_Release(stream->descriptor); + goto done; + } + + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0]))) + { + IMFStreamDescriptor_Release(stream->descriptor); + goto done; + } + +done: + if (type_handler) + IMFMediaTypeHandler_Release(type_handler); + for (i = 0; i < type_count; i++) + IMFMediaType_Release(stream_types[i]); + return hr; +} + +static HRESULT WINAPI media_source_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_get_service_AddRef(IMFGetService *iface) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_get_service_Release(IMFGetService *iface) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFGetService(iface); + + TRACE("%p, %s, %s, %p.\n", iface, debugstr_guid(service), debugstr_guid(riid), obj); + + *obj = NULL; + + if (IsEqualGUID(service, &MF_RATE_CONTROL_SERVICE)) + { + if (IsEqualIID(riid, &IID_IMFRateSupport)) + { + *obj = &source->IMFRateSupport_iface; + } + else if (IsEqualIID(riid, &IID_IMFRateControl)) + { + *obj = &source->IMFRateControl_iface; + } + } + else + FIXME("Unsupported service %s.\n", debugstr_guid(service)); + + if (*obj) + IUnknown_AddRef((IUnknown *)*obj); + + return *obj ? S_OK : E_NOINTERFACE; +} + +static const IMFGetServiceVtbl media_source_get_service_vtbl = +{ + media_source_get_service_QueryInterface, + media_source_get_service_AddRef, + media_source_get_service_Release, + media_source_get_service_GetService, +}; + +static HRESULT WINAPI media_source_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_rate_support_AddRef(IMFRateSupport *iface) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_rate_support_Release(IMFRateSupport *iface) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_rate_support_GetSlowestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) +{ + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + *rate = 0.0f; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_support_GetFastestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) +{ + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + *rate = direction == MFRATE_FORWARD ? 1e6f : -1e6f; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate, + float *nearest_rate) +{ + TRACE("%p, %d, %f, %p.\n", iface, thin, rate, nearest_rate); + + if (nearest_rate) + *nearest_rate = rate; + + return rate >= -1e6f && rate <= 1e6f ? S_OK : MF_E_UNSUPPORTED_RATE; +} + +static const IMFRateSupportVtbl media_source_rate_support_vtbl = +{ + media_source_rate_support_QueryInterface, + media_source_rate_support_AddRef, + media_source_rate_support_Release, + media_source_rate_support_GetSlowestRate, + media_source_rate_support_GetFastestRate, + media_source_rate_support_IsRateSupported, +}; + +static HRESULT WINAPI media_source_rate_control_QueryInterface(IMFRateControl *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_rate_control_AddRef(IMFRateControl *iface) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_rate_control_Release(IMFRateControl *iface) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_rate_control_SetRate(IMFRateControl *iface, BOOL thin, float rate) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + HRESULT hr; + + FIXME("%p, %d, %f.\n", iface, thin, rate); + + if (rate < 0.0f) + return MF_E_REVERSE_UNSUPPORTED; + + if (thin) + return MF_E_THINNING_UNSUPPORTED; + + if (FAILED(hr = IMFRateSupport_IsRateSupported(&source->IMFRateSupport_iface, thin, rate, NULL))) + return hr; + + EnterCriticalSection(&source->cs); + source->rate = rate; + LeaveCriticalSection(&source->cs); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceRateChanged, &GUID_NULL, S_OK, NULL); +} + +static HRESULT WINAPI media_source_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + + TRACE("%p, %p, %p.\n", iface, thin, rate); + + if (thin) + *thin = FALSE; + + EnterCriticalSection(&source->cs); + *rate = source->rate; + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static const IMFRateControlVtbl media_source_rate_control_vtbl = +{ + media_source_rate_control_QueryInterface, + media_source_rate_control_AddRef, + media_source_rate_control_Release, + media_source_rate_control_SetRate, + media_source_rate_control_GetRate, +}; + +static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &source->IMFMediaSource_iface; + } + else if (IsEqualIID(riid, &IID_IMFGetService)) + { + *out = &source->IMFGetService_iface; + } + else + { + FIXME("%s, %p.\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&source->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&source->ref); + + TRACE("%p, refcount %lu.\n", iface, ref); + + if (!ref) + { + IMFMediaSource_Shutdown(iface); + IMFPresentationDescriptor_Release(source->pres_desc); + IMFMediaEventQueue_Release(source->event_queue); + IMFByteStream_Release(source->byte_stream); + wg_parser_destroy(source->wg_parser); + source->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&source->cs); + free(source); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", iface, characteristics); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + *characteristics = MFMEDIASOURCE_CAN_SEEK | MFMEDIASOURCE_CAN_PAUSE; + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr; + + TRACE("%p, %p.\n", iface, descriptor); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + hr = IMFPresentationDescriptor_Clone(source->pres_desc, descriptor); + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, + const GUID *time_format, const PROPVARIANT *position) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, descriptor, time_format, position); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (!(IsEqualIID(time_format, &GUID_NULL))) + hr = MF_E_UNSUPPORTED_TIME_FORMAT; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command))) + { + command->u.start.descriptor = descriptor; + command->u.start.format = *time_format; + PropVariantCopy(&command->u.start.position, position); + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + } + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_STOP, &command))) + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + + LeaveCriticalSection(&source->cs); + + return hr; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (source->state != SOURCE_RUNNING) + hr = MF_E_INVALID_STATE_TRANSITION; + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &command))) + hr = MFPutWorkItem(source->async_commands_queue, + &source->async_commands_callback, &command->IUnknown_iface); + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p.\n", iface); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + { + LeaveCriticalSection(&source->cs); + return MF_E_SHUTDOWN; + } + + source->state = SOURCE_SHUTDOWN; + + wg_parser_disconnect(source->wg_parser); + + source->read_thread_shutdown = true; + WaitForSingleObject(source->read_thread, INFINITE); + CloseHandle(source->read_thread); + + IMFMediaEventQueue_Shutdown(source->event_queue); + IMFByteStream_Close(source->byte_stream); + + while (source->stream_count--) + { + struct media_stream *stream = source->streams[source->stream_count]; + IMFMediaEventQueue_Shutdown(stream->event_queue); + IMFMediaStream_Release(&stream->IMFMediaStream_iface); + } + free(source->streams); + + MFUnlockWorkQueue(source->async_commands_queue); + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +static const IMFMediaSourceVtbl IMFMediaSource_vtbl = +{ + media_source_QueryInterface, + media_source_AddRef, + media_source_Release, + media_source_GetEvent, + media_source_BeginGetEvent, + media_source_EndGetEvent, + media_source_QueueEvent, + media_source_GetCharacteristics, + media_source_CreatePresentationDescriptor, + media_source_Start, + media_source_Stop, + media_source_Pause, + media_source_Shutdown, +}; + +HRESULT media_source_create_old(IMFByteStream *bytestream, const WCHAR *uri, IMFMediaSource **out) +{ + BOOL video_selected = FALSE, audio_selected = FALSE; + IMFStreamDescriptor **descriptors = NULL; + unsigned int stream_count = UINT_MAX; + struct media_source *object; + UINT64 total_pres_time = 0; + struct wg_parser *parser; + DWORD bytestream_caps; + uint64_t file_size; + unsigned int i; + HRESULT hr; + + if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &bytestream_caps))) + return hr; + + if (!(bytestream_caps & MFBYTESTREAM_IS_SEEKABLE)) + { + FIXME("Non-seekable bytestreams not supported.\n"); + return MF_E_BYTESTREAM_NOT_SEEKABLE; + } + + if (FAILED(hr = IMFByteStream_GetLength(bytestream, &file_size))) + { + FIXME("Failed to get byte stream length, hr %#lx.\n", hr); + return hr; + } + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + object->IMFGetService_iface.lpVtbl = &media_source_get_service_vtbl; + object->IMFRateSupport_iface.lpVtbl = &media_source_rate_support_vtbl; + object->IMFRateControl_iface.lpVtbl = &media_source_rate_control_vtbl; + object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl; + object->ref = 1; + object->byte_stream = bytestream; + IMFByteStream_AddRef(bytestream); + object->rate = 1.0f; + InitializeCriticalSection(&object->cs); + object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto fail; + + if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) + goto fail; + + if (!(parser = wg_parser_create(uri ? WG_PARSER_URIDECODEBIN : WG_PARSER_DECODEBIN, false))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + object->wg_parser = parser; + + object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL); + + object->state = SOURCE_OPENING; + + if (FAILED(hr = wg_parser_connect(parser, file_size, uri))) + goto fail; + + stream_count = wg_parser_get_stream_count(parser); + + if (!(object->streams = calloc(stream_count, sizeof(*object->streams)))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + + for (i = 0; i < stream_count; ++i) + { + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, i, &object->streams[i]))) + goto fail; + + if (FAILED(hr = media_stream_init_desc(object->streams[i]))) + { + ERR("Failed to finish initialization of media stream %p, hr %#lx.\n", object->streams[i], hr); + IMFMediaSource_Release(object->streams[i]->media_source); + IMFMediaEventQueue_Release(object->streams[i]->event_queue); + free(object->streams[i]); + goto fail; + } + + object->stream_count++; + } + + /* init presentation descriptor */ + + descriptors = malloc(object->stream_count * sizeof(IMFStreamDescriptor *)); + for (i = 0; i < object->stream_count; i++) + { + static const struct + { + enum wg_parser_tag tag; + const GUID *mf_attr; + } + tags[] = + { + {WG_PARSER_TAG_LANGUAGE, &MF_SD_LANGUAGE}, + {WG_PARSER_TAG_NAME, &MF_SD_STREAM_NAME}, + }; + IMFStreamDescriptor **descriptor = descriptors + object->stream_count - 1 - i; + unsigned int j; + WCHAR *strW; + DWORD len; + char *str; + + IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, descriptor); + + for (j = 0; j < ARRAY_SIZE(tags); ++j) + { + if (!(str = wg_parser_stream_get_tag(object->streams[i]->wg_stream, tags[j].tag))) + continue; + if (!(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) + { + free(str); + continue; + } + strW = malloc(len * sizeof(*strW)); + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) + IMFStreamDescriptor_SetString(*descriptor, tags[j].mf_attr, strW); + free(strW); + free(str); + } + } + + if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc))) + goto fail; + + /* Select one of each major type. */ + for (i = 0; i < object->stream_count; i++) + { + IMFMediaTypeHandler *handler; + GUID major_type; + BOOL select_stream = FALSE; + + IMFStreamDescriptor_GetMediaTypeHandler(descriptors[i], &handler); + IMFMediaTypeHandler_GetMajorType(handler, &major_type); + if (IsEqualGUID(&major_type, &MFMediaType_Video) && !video_selected) + { + select_stream = TRUE; + video_selected = TRUE; + } + if (IsEqualGUID(&major_type, &MFMediaType_Audio) && !audio_selected) + { + select_stream = TRUE; + audio_selected = TRUE; + } + if (select_stream) + IMFPresentationDescriptor_SelectStream(object->pres_desc, i); + IMFMediaTypeHandler_Release(handler); + IMFStreamDescriptor_Release(descriptors[i]); + } + free(descriptors); + descriptors = NULL; + + for (i = 0; i < object->stream_count; i++) + total_pres_time = max(total_pres_time, + wg_parser_stream_get_duration(object->streams[i]->wg_stream)); + + if (object->stream_count) + IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time); + + object->state = SOURCE_STOPPED; + + *out = &object->IMFMediaSource_iface; + return S_OK; + + fail: + WARN("Failed to construct MFMediaSource, hr %#lx.\n", hr); + + if (descriptors) + { + for (i = 0; i < object->stream_count; i++) + IMFStreamDescriptor_Release(descriptors[i]); + free(descriptors); + } + + while (object->streams && object->stream_count--) + { + struct media_stream *stream = object->streams[object->stream_count]; + IMFMediaStream_Release(&stream->IMFMediaStream_iface); + } + free(object->streams); + + if (stream_count != UINT_MAX) + wg_parser_disconnect(object->wg_parser); + if (object->read_thread) + { + object->read_thread_shutdown = true; + WaitForSingleObject(object->read_thread, INFINITE); + CloseHandle(object->read_thread); + } + if (object->wg_parser) + wg_parser_destroy(object->wg_parser); + if (object->async_commands_queue) + MFUnlockWorkQueue(object->async_commands_queue); + if (object->event_queue) + IMFMediaEventQueue_Release(object->event_queue); + IMFByteStream_Release(object->byte_stream); + free(object); + return hr; +} + +HRESULT media_source_create_from_url(const WCHAR *url, IMFMediaSource **out) +{ + IMFByteStream *bytestream; + IStream *stream; + HRESULT hr; + + if (FAILED(hr = CreateStreamOnHGlobal(0, TRUE, &stream))) + return hr; + + hr = MFCreateMFByteStreamOnStream(stream, &bytestream); + IStream_Release(stream); + if (FAILED(hr)) + return hr; + + hr = media_source_create_old(bytestream, url, out); + IMFByteStream_Release(bytestream); + + return hr; +} diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index 3d7f36e749c..7472f8c324a 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -173,10 +173,16 @@ static HRESULT async_create_object_complete(struct async_create_object *async, if (async->flags & MF_RESOLUTION_MEDIASOURCE) { + const char *env = getenv("WINE_NEW_MEDIA_SOURCE"); if (!async->stream) hr = media_source_create_from_url(async->url, (IMFMediaSource **)&object); - else - hr = media_source_create(async->stream, NULL, (IMFMediaSource **)&object); + else if (!env || !atoi(env)) + hr = media_source_create_old(async->stream, NULL, (IMFMediaSource **)&object); + else if (FAILED(hr = media_source_create(async->stream, async->url, async->buffer, async->size, (IMFMediaSource **)&object))) + { + FIXME("Failed to create new media source, falling back to old implementation, hr %#lx\n", hr); + hr = media_source_create_old(async->stream, NULL, (IMFMediaSource **)&object); + } } else { From fa9be038dc191849c8901467cd1561df4acda631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:03 +0200 Subject: [PATCH 345/758] Revert "HACK: winegstreamer: Expose S16LE as the first native media type in MFSourceReader." This reverts commit f5f7e7f9b5699d6be6956836b6655c814a9b3efc. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ec1223c7bfd..5832d9c912e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -937,32 +937,26 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) else if (format.major_type == WG_MAJOR_TYPE_AUDIO) { /* Expose at least one PCM and one floating point type for the - consumer to pick from. Moreover, ensure that we expose S16LE first, - as games such as MGSV expect the native media type to be 16 bps. */ + consumer to pick from. */ static const enum wg_audio_format audio_types[] = { WG_AUDIO_FORMAT_S16LE, WG_AUDIO_FORMAT_F32LE, }; - BOOL has_native_format = FALSE; + if ((stream_types[0] = mf_media_type_from_wg_format(&format))) + type_count = 1; for (i = 0; i < ARRAY_SIZE(audio_types); i++) { struct wg_format new_format; - + if (format.u.audio.format == audio_types[i]) + continue; new_format = format; new_format.u.audio.format = audio_types[i]; if ((stream_types[type_count] = mf_media_type_from_wg_format(&new_format))) - { - if (format.u.audio.format == audio_types[i]) - has_native_format = TRUE; type_count++; - } } - - if (!has_native_format && (stream_types[type_count] = mf_media_type_from_wg_format(&format))) - type_count++; } else { From 5775e61441acb887506ae56c12d591bd8841a688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:12 +0200 Subject: [PATCH 346/758] Revert "winegstreamer: Add MF_MT_VIDEO_NOMINAL_RANGE attribute to base video output type." This reverts commit ab7bba8affa3f99c71daa70a202b526a91bff806. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5832d9c912e..40f989781b3 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -910,8 +910,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) goto done; } - IMFMediaType_SetUINT32(base_type, &MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Normal); - IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype); stream_types[0] = base_type; From d6a5599a00e71870ce640a7a6b33349ad58a5230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:15 +0200 Subject: [PATCH 347/758] Revert "winegstreamer: Add MFVideoFormat_ABGR32 media type to media source video stream descriptor." This reverts commit 08929931ec120b7370696dca5943b2da64b4cd7e. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 40f989781b3..00840f8b2ec 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -27,8 +27,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); -extern const GUID MFVideoFormat_ABGR32; - struct media_stream { IMFMediaStream IMFMediaStream_iface; @@ -877,7 +875,7 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, static HRESULT media_stream_init_desc(struct media_stream *stream) { IMFMediaTypeHandler *type_handler = NULL; - IMFMediaType *stream_types[9]; + IMFMediaType *stream_types[8]; struct wg_format format; DWORD type_count = 0; HRESULT hr = S_OK; @@ -898,7 +896,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) &MFVideoFormat_I420, &MFVideoFormat_ARGB32, &MFVideoFormat_RGB32, - &MFVideoFormat_ABGR32, }; IMFMediaType *base_type = mf_media_type_from_wg_format(&format); From ac8837c74b575e0cea0135f9a3f2d686b6486d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:27 +0200 Subject: [PATCH 348/758] Revert "winegstreamer: Add MFVideoFormat_RGB32 output for the source." This reverts commit 1f337574a6a6bbd09ae80e818c20b80dfaddc91b. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 00840f8b2ec..732285ad09f 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -875,7 +875,7 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, static HRESULT media_stream_init_desc(struct media_stream *stream) { IMFMediaTypeHandler *type_handler = NULL; - IMFMediaType *stream_types[8]; + IMFMediaType *stream_types[7]; struct wg_format format; DWORD type_count = 0; HRESULT hr = S_OK; @@ -895,7 +895,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) &MFVideoFormat_IYUV, &MFVideoFormat_I420, &MFVideoFormat_ARGB32, - &MFVideoFormat_RGB32, }; IMFMediaType *base_type = mf_media_type_from_wg_format(&format); From 23cfbaaf9082a7fe1beda9c0eb077c209f1f5d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:30 +0200 Subject: [PATCH 349/758] Revert "winegstreamer: Add MFVideoFormat_ARGB32 output for the source." This reverts commit 39e48f1a2ac64d43694a7baa5ae7ebe84b866550. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 732285ad09f..36d3d2674ad 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -875,7 +875,7 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, static HRESULT media_stream_init_desc(struct media_stream *stream) { IMFMediaTypeHandler *type_handler = NULL; - IMFMediaType *stream_types[7]; + IMFMediaType *stream_types[6]; struct wg_format format; DWORD type_count = 0; HRESULT hr = S_OK; @@ -894,7 +894,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) &MFVideoFormat_YUY2, &MFVideoFormat_IYUV, &MFVideoFormat_I420, - &MFVideoFormat_ARGB32, }; IMFMediaType *base_type = mf_media_type_from_wg_format(&format); From c7de925390eacf15d99212c7e97e0a5920820578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:34 +0200 Subject: [PATCH 350/758] Revert "winegstreamer: In the default configuration, select one stream of each major type." This reverts commit bedb7b53764576d95dd01ea99fc6f290edc9ed3a. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 36d3d2674ad..8e0c2b4068f 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1430,7 +1430,6 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { - BOOL video_selected = FALSE, audio_selected = FALSE; IMFStreamDescriptor **descriptors = NULL; unsigned int stream_count = UINT_MAX; struct media_source *object; @@ -1559,28 +1558,9 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc))) goto fail; - /* Select one of each major type. */ for (i = 0; i < object->stream_count; i++) { - IMFMediaTypeHandler *handler; - GUID major_type; - BOOL select_stream = FALSE; - - IMFStreamDescriptor_GetMediaTypeHandler(descriptors[i], &handler); - IMFMediaTypeHandler_GetMajorType(handler, &major_type); - if (IsEqualGUID(&major_type, &MFMediaType_Video) && !video_selected) - { - select_stream = TRUE; - video_selected = TRUE; - } - if (IsEqualGUID(&major_type, &MFMediaType_Audio) && !audio_selected) - { - select_stream = TRUE; - audio_selected = TRUE; - } - if (select_stream) - IMFPresentationDescriptor_SelectStream(object->pres_desc, i); - IMFMediaTypeHandler_Release(handler); + IMFPresentationDescriptor_SelectStream(object->pres_desc, i); IMFStreamDescriptor_Release(descriptors[i]); } free(descriptors); From fba1486a88ff22c3a62bfe7d4d9797bcda715da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:37 +0200 Subject: [PATCH 351/758] Revert "winegstreamer: Report streams backwards in media source." This reverts commit 4f3d59529f9c4dc9f06cae344cf796275b85558e. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 8e0c2b4068f..2c397dd6be5 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1530,13 +1530,12 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d {WG_PARSER_TAG_LANGUAGE, &MF_SD_LANGUAGE}, {WG_PARSER_TAG_NAME, &MF_SD_STREAM_NAME}, }; - IMFStreamDescriptor **descriptor = descriptors + object->stream_count - 1 - i; unsigned int j; WCHAR *strW; DWORD len; char *str; - IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, descriptor); + IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]); for (j = 0; j < ARRAY_SIZE(tags); ++j) { @@ -1549,7 +1548,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d } strW = malloc(len * sizeof(*strW)); if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) - IMFStreamDescriptor_SetString(*descriptor, tags[j].mf_attr, strW); + IMFStreamDescriptor_SetString(descriptors[i], tags[j].mf_attr, strW); free(strW); free(str); } From b551650783ee6ce86330e53f7fa5fac86993b549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 17 Apr 2023 12:20:25 +0200 Subject: [PATCH 352/758] winegstreamer: Return a IUnknown pointer from source_create_async_op. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 41 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 2c397dd6be5..efde37f7523 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -191,7 +191,7 @@ static const IUnknownVtbl source_async_command_vtbl = source_async_command_Release, }; -static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret) +static HRESULT source_create_async_op(enum source_async_op op, IUnknown **out) { struct source_async_command *command; @@ -201,8 +201,7 @@ static HRESULT source_create_async_op(enum source_async_op op, struct source_asy command->IUnknown_iface.lpVtbl = &source_async_command_vtbl; command->op = op; - *ret = command; - + *out = &command->IUnknown_iface; return S_OK; } @@ -293,15 +292,16 @@ static void flush_token_queue(struct media_stream *stream, BOOL send) { if (send) { + IUnknown *op; HRESULT hr; - struct source_async_command *command; - if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op))) { + struct source_async_command *command = impl_from_async_command_IUnknown(op); command->u.request_sample.stream = stream; command->u.request_sample.token = stream->token_queue[i]; - hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, - &command->IUnknown_iface); + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); } if (FAILED(hr)) WARN("Could not enqueue sample request, hr %#lx\n", hr); @@ -795,7 +795,7 @@ static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown { struct media_stream *stream = impl_from_IMFMediaStream(iface); struct media_source *source = impl_from_IMFMediaSource(stream->media_source); - struct source_async_command *command; + IUnknown *op; HRESULT hr; TRACE("%p, %p.\n", iface, token); @@ -808,14 +808,15 @@ static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown hr = MF_E_MEDIA_SOURCE_WRONGSTATE; else if (stream->eos) hr = MF_E_END_OF_STREAM; - else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op))) { + struct source_async_command *command = impl_from_async_command_IUnknown(op); command->u.request_sample.stream = stream; if (token) IUnknown_AddRef(token); command->u.request_sample.token = token; - hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); } LeaveCriticalSection(&source->cs); @@ -1303,7 +1304,7 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD const GUID *time_format, const PROPVARIANT *position) { struct media_source *source = impl_from_IMFMediaSource(iface); - struct source_async_command *command; + IUnknown *op; HRESULT hr; TRACE("%p, %p, %p, %p.\n", iface, descriptor, time_format, position); @@ -1314,13 +1315,14 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD hr = MF_E_SHUTDOWN; else if (!(IsEqualIID(time_format, &GUID_NULL))) hr = MF_E_UNSUPPORTED_TIME_FORMAT; - else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command))) + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &op))) { + struct source_async_command *command = impl_from_async_command_IUnknown(op); command->u.start.descriptor = descriptor; command->u.start.format = *time_format; PropVariantCopy(&command->u.start.position, position); - hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); } LeaveCriticalSection(&source->cs); @@ -1331,7 +1333,7 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) { struct media_source *source = impl_from_IMFMediaSource(iface); - struct source_async_command *command; + IUnknown *op; HRESULT hr; TRACE("%p.\n", iface); @@ -1340,8 +1342,8 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) if (source->state == SOURCE_SHUTDOWN) hr = MF_E_SHUTDOWN; - else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_STOP, &command))) - hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_STOP, &op))) + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); LeaveCriticalSection(&source->cs); @@ -1351,7 +1353,7 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) { struct media_source *source = impl_from_IMFMediaSource(iface); - struct source_async_command *command; + IUnknown *op; HRESULT hr; TRACE("%p.\n", iface); @@ -1362,9 +1364,8 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) hr = MF_E_SHUTDOWN; else if (source->state != SOURCE_RUNNING) hr = MF_E_INVALID_STATE_TRANSITION; - else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &command))) - hr = MFPutWorkItem(source->async_commands_queue, - &source->async_commands_callback, &command->IUnknown_iface); + else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &op))) + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); LeaveCriticalSection(&source->cs); From 20f9dac9e5dc218d14da8d9fef6ac9a0cf47d529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 21:52:36 +0200 Subject: [PATCH 353/758] winegstreamer: Avoid leaking media source async commands. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index efde37f7523..2e1221d1fed 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -199,6 +199,7 @@ static HRESULT source_create_async_op(enum source_async_op op, IUnknown **out) return E_OUTOFMEMORY; command->IUnknown_iface.lpVtbl = &source_async_command_vtbl; + command->refcount = 1; command->op = op; *out = &command->IUnknown_iface; @@ -302,6 +303,7 @@ static void flush_token_queue(struct media_stream *stream, BOOL send) command->u.request_sample.token = stream->token_queue[i]; hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); } if (FAILED(hr)) WARN("Could not enqueue sample request, hr %#lx\n", hr); @@ -817,6 +819,7 @@ static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown command->u.request_sample.token = token; hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); } LeaveCriticalSection(&source->cs); @@ -1323,6 +1326,7 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD PropVariantCopy(&command->u.start.position, position); hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); } LeaveCriticalSection(&source->cs); @@ -1343,7 +1347,10 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) if (source->state == SOURCE_SHUTDOWN) hr = MF_E_SHUTDOWN; else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_STOP, &op))) + { hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } LeaveCriticalSection(&source->cs); @@ -1365,7 +1372,10 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) else if (source->state != SOURCE_RUNNING) hr = MF_E_INVALID_STATE_TRANSITION; else if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &op))) + { hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, op); + IUnknown_Release(op); + } LeaveCriticalSection(&source->cs); From 47f9135316941af37481dce73eff7e1eb713a1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 11:40:19 +0200 Subject: [PATCH 354/758] winegstreamer: Convert stream descriptor media type to wg_format using helpers. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 41 ++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 2e1221d1fed..15c8721c83f 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -241,6 +241,32 @@ static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *ifa return IMFMediaSource_Release(&source->IMFMediaSource_iface); } +static HRESULT stream_descriptor_get_media_type(IMFStreamDescriptor *descriptor, IMFMediaType **media_type) +{ + IMFMediaTypeHandler *handler; + HRESULT hr; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(descriptor, &handler))) + return hr; + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, media_type); + IMFMediaTypeHandler_Release(handler); + + return hr; +} + +static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, struct wg_format *format) +{ + IMFMediaType *media_type; + HRESULT hr; + + if (FAILED(hr = stream_descriptor_get_media_type(descriptor, &media_type))) + return hr; + mf_media_type_to_wg_format(media_type, format); + IMFMediaType_Release(media_type); + + return hr; +} + static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected) { ULONG sd_count; @@ -322,6 +348,7 @@ static void start_pipeline(struct media_source *source, struct source_async_comm PROPVARIANT *position = &command->u.start.position; BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY; unsigned int i; + HRESULT hr; /* seek to beginning on stop->play */ if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) @@ -334,8 +361,7 @@ static void start_pipeline(struct media_source *source, struct source_async_comm { struct media_stream *stream; IMFStreamDescriptor *sd; - IMFMediaTypeHandler *mth; - IMFMediaType *current_mt; + struct wg_format format; DWORD stream_id; BOOL was_active; BOOL selected; @@ -352,16 +378,9 @@ static void start_pipeline(struct media_source *source, struct source_async_comm if (selected) { - struct wg_format format; - - IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth); - IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt); - - mf_media_type_to_wg_format(current_mt, &format); + if (FAILED(hr = wg_format_from_stream_descriptor(stream->descriptor, &format))) + WARN("Failed to get wg_format from stream descriptor, hr %#lx\n", hr); wg_parser_stream_enable(stream->wg_stream, &format, 0); - - IMFMediaType_Release(current_mt); - IMFMediaTypeHandler_Release(mth); } else { From c7c86ed305d6b7afc7a75024090625f43665c1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 11:59:32 +0200 Subject: [PATCH 355/758] winegstreamer: Start media streams in a dedicated media_stream_start helper. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 53 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 15c8721c83f..f88ce18f6d5 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -343,6 +343,25 @@ static void flush_token_queue(struct media_stream *stream, BOOL send) stream->token_queue_cap = 0; } +static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL seeking, const PROPVARIANT *position) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct wg_format format; + HRESULT hr; + + TRACE("source %p, stream %p\n", source, stream); + + if (FAILED(hr = wg_format_from_stream_descriptor(stream->descriptor, &format))) + WARN("Failed to get wg_format from stream descriptor, hr %#lx\n", hr); + wg_parser_stream_enable(stream->wg_stream, &format, 0); + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, active ? MEUpdatedStream : MENewStream, + &GUID_NULL, S_OK, (IUnknown *)&stream->IMFMediaStream_iface))) + WARN("Failed to send source stream event, hr %#lx\n", hr); + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, seeking ? MEStreamSeeked : MEStreamStarted, + &GUID_NULL, S_OK, position); +} + static void start_pipeline(struct media_source *source, struct source_async_command *command) { PROPVARIANT *position = &command->u.start.position; @@ -360,46 +379,24 @@ static void start_pipeline(struct media_source *source, struct source_async_comm for (i = 0; i < source->stream_count; i++) { struct media_stream *stream; + BOOL was_active, selected; IMFStreamDescriptor *sd; - struct wg_format format; DWORD stream_id; - BOOL was_active; - BOOL selected; stream = source->streams[i]; + was_active = stream->active; IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); - sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected); IMFStreamDescriptor_Release(sd); - was_active = stream->active; - stream->active = selected; - - if (selected) - { - if (FAILED(hr = wg_format_from_stream_descriptor(stream->descriptor, &format))) - WARN("Failed to get wg_format from stream descriptor, hr %#lx\n", hr); - wg_parser_stream_enable(stream->wg_stream, &format, 0); - } - else - { - wg_parser_stream_disable(stream->wg_stream); - } - if (position->vt != VT_EMPTY) stream->eos = FALSE; - if (selected) - { - TRACE("Stream %u (%p) selected\n", i, stream); - IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, - was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, - S_OK, (IUnknown*) &stream->IMFMediaStream_iface); - - IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, - seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position); - } + if (!(stream->active = selected)) + wg_parser_stream_disable(stream->wg_stream); + else if (FAILED(hr = media_stream_start(stream, was_active, seek_message, position))) + WARN("Failed to start media stream, hr %#lx\n", hr); } IMFMediaEventQueue_QueueEventParamVar(source->event_queue, From 61e89c6396698e917a38e9c944e7c7ae091b627f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 11:04:36 +0200 Subject: [PATCH 356/758] winegstreamer: Simplify media source wait_on_sample control flow. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 113 ++++++++++++------------------ 1 file changed, 43 insertions(+), 70 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index f88ce18f6d5..a995ec6bdff 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -452,59 +452,19 @@ static void stop_pipeline(struct media_source *source) flush_token_queue(source->streams[i], FALSE); } -static void dispatch_end_of_presentation(struct media_source *source) -{ - PROPVARIANT empty = {.vt = VT_EMPTY}; - unsigned int i; - - /* A stream has ended, check whether all have */ - for (i = 0; i < source->stream_count; i++) - { - struct media_stream *stream = source->streams[i]; - if (stream->active && !stream->eos) - return; - } - - IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); -} - -static void send_buffer(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) +static HRESULT media_stream_send_sample(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) { + IMFSample *sample = NULL; IMFMediaBuffer *buffer; - IMFSample *sample; HRESULT hr; BYTE *data; - if (FAILED(hr = MFCreateSample(&sample))) - { - ERR("Failed to create sample, hr %#lx.\n", hr); - return; - } - if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer->size, &buffer))) - { - ERR("Failed to create buffer, hr %#lx.\n", hr); - IMFSample_Release(sample); - return; - } - - if (FAILED(hr = IMFSample_AddBuffer(sample, buffer))) - { - ERR("Failed to add buffer, hr %#lx.\n", hr); - goto out; - } - + return hr; if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer->size))) - { - ERR("Failed to set size, hr %#lx.\n", hr); goto out; - } - if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) - { - ERR("Failed to lock buffer, hr %#lx.\n", hr); goto out; - } if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) { @@ -515,52 +475,62 @@ static void send_buffer(struct media_stream *stream, const struct wg_parser_buff wg_parser_stream_release_buffer(stream->wg_stream); if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) - { - ERR("Failed to unlock buffer, hr %#lx.\n", hr); goto out; - } + if (FAILED(hr = MFCreateSample(&sample))) + goto out; + if (FAILED(hr = IMFSample_AddBuffer(sample, buffer))) + goto out; if (FAILED(hr = IMFSample_SetSampleTime(sample, wg_buffer->pts))) - { - ERR("Failed to set sample time, hr %#lx.\n", hr); goto out; - } - if (FAILED(hr = IMFSample_SetSampleDuration(sample, wg_buffer->duration))) - { - ERR("Failed to set sample duration, hr %#lx.\n", hr); goto out; - } - - if (token) - IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + if (token && FAILED(hr = IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token))) + goto out; - IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); out: + if (sample) + IMFSample_Release(sample); IMFMediaBuffer_Release(buffer); - IMFSample_Release(sample); + return hr; +} + +static HRESULT media_stream_send_eos(struct media_source *source, struct media_stream *stream) +{ + PROPVARIANT empty = {.vt = VT_EMPTY}; + HRESULT hr; + UINT i; + + stream->eos = TRUE; + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty))) + WARN("Failed to queue MEEndOfStream event, hr %#lx\n", hr); + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active && !stream->eos) + return S_OK; + } + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty))) + WARN("Failed to queue MEEndOfPresentation event, hr %#lx\n", hr); + return S_OK; } -static void wait_on_sample(struct media_stream *stream, IUnknown *token) +static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) { struct media_source *source = impl_from_IMFMediaSource(stream->media_source); - PROPVARIANT empty_var = {.vt = VT_EMPTY}; struct wg_parser_buffer buffer; TRACE("%p, %p\n", stream, token); if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) - { - send_buffer(stream, &buffer, token); - } - else - { - stream->eos = TRUE; - IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); - dispatch_end_of_presentation(source); - } + return media_stream_send_sample(stream, &buffer, token); + + return media_stream_send_eos(source, stream); } static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) @@ -594,7 +564,10 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA if (source->state == SOURCE_PAUSED) enqueue_token(command->u.request_sample.stream, command->u.request_sample.token); else if (source->state == SOURCE_RUNNING) - wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token); + { + if (FAILED(hr = wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token))) + WARN("Failed to request sample, hr %#lx\n", hr); + } break; } From e0399862a22b89b6da85a6b4bd872b99cfe36cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 12:00:49 +0200 Subject: [PATCH 357/758] winegstreamer: Avoid eating errors in media source async commands. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 60 +++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index a995ec6bdff..6c94c9d196c 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -362,13 +362,19 @@ static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL &GUID_NULL, S_OK, position); } -static void start_pipeline(struct media_source *source, struct source_async_command *command) +static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, + GUID *format, PROPVARIANT *position) { - PROPVARIANT *position = &command->u.start.position; BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY; unsigned int i; HRESULT hr; + TRACE("source %p, descriptor %p, format %s, position %s\n", source, descriptor, + debugstr_guid(format), wine_dbgstr_variant((VARIANT *)position)); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + /* seek to beginning on stop->play */ if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) { @@ -387,7 +393,7 @@ static void start_pipeline(struct media_source *source, struct source_async_comm was_active = stream->active; IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); - sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected); + sd = stream_descriptor_from_id(descriptor, stream_id, &selected); IMFStreamDescriptor_Release(sd); if (position->vt != VT_EMPTY) @@ -399,10 +405,6 @@ static void start_pipeline(struct media_source *source, struct source_async_comm WARN("Failed to start media stream, hr %#lx\n", hr); } - IMFMediaEventQueue_QueueEventParamVar(source->event_queue, - seek_message ? MESourceSeeked : MESourceStarted, - &GUID_NULL, S_OK, position); - source->state = SOURCE_RUNNING; if (position->vt == VT_I8) @@ -411,13 +413,21 @@ static void start_pipeline(struct media_source *source, struct source_async_comm for (i = 0; i < source->stream_count; i++) flush_token_queue(source->streams[i], position->vt == VT_EMPTY); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + seek_message ? MESourceSeeked : MESourceStarted, &GUID_NULL, S_OK, position); } -static void pause_pipeline(struct media_source *source) +static HRESULT media_source_pause(struct media_source *source) { unsigned int i; HRESULT hr; + TRACE("source %p\n", source); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + for (i = 0; i < source->stream_count; i++) { struct media_stream *stream = source->streams[i]; @@ -426,16 +436,20 @@ static void pause_pipeline(struct media_source *source) WARN("Failed to queue MEStreamPaused event, hr %#lx\n", hr); } - IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourcePaused, &GUID_NULL, S_OK, NULL); - source->state = SOURCE_PAUSED; + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourcePaused, &GUID_NULL, S_OK, NULL); } -static void stop_pipeline(struct media_source *source) +static HRESULT media_source_stop(struct media_source *source) { unsigned int i; HRESULT hr; + TRACE("source %p\n", source); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + for (i = 0; i < source->stream_count; i++) { struct media_stream *stream = source->streams[i]; @@ -444,12 +458,12 @@ static void stop_pipeline(struct media_source *source) WARN("Failed to queue MEStreamStopped event, hr %#lx\n", hr); } - IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); - source->state = SOURCE_STOPPED; for (i = 0; i < source->stream_count; i++) flush_token_queue(source->streams[i], FALSE); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); } static HRESULT media_stream_send_sample(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) @@ -504,6 +518,8 @@ static HRESULT media_stream_send_eos(struct media_source *source, struct media_s HRESULT hr; UINT i; + TRACE("source %p, stream %p\n", source, stream); + stream->eos = TRUE; if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty))) WARN("Failed to queue MEEndOfStream event, hr %#lx\n", hr); @@ -549,16 +565,22 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA switch (command->op) { case SOURCE_ASYNC_START: - if (source->state != SOURCE_SHUTDOWN) - start_pipeline(source, command); + { + IMFPresentationDescriptor *descriptor = command->u.start.descriptor; + GUID format = command->u.start.format; + PROPVARIANT position = command->u.start.position; + + if (FAILED(hr = media_source_start(source, descriptor, &format, &position))) + WARN("Failed to start source %p, hr %#lx\n", source, hr); break; + } case SOURCE_ASYNC_PAUSE: - if (source->state != SOURCE_SHUTDOWN) - pause_pipeline(source); + if (FAILED(hr = media_source_pause(source))) + WARN("Failed to pause source %p, hr %#lx\n", source, hr); break; case SOURCE_ASYNC_STOP: - if (source->state != SOURCE_SHUTDOWN) - stop_pipeline(source); + if (FAILED(hr = media_source_stop(source))) + WARN("Failed to stop source %p, hr %#lx\n", source, hr); break; case SOURCE_ASYNC_REQUEST_SAMPLE: if (source->state == SOURCE_PAUSED) From abe7ed6bb7d53a482f63b08a59e852b9355aa3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 18:41:17 +0200 Subject: [PATCH 358/758] winegstreamer: Keep a stream descriptor array on the media source. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 48 ++++++++++++++----------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 6c94c9d196c..db92219182a 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -92,8 +92,10 @@ struct media_source struct wg_parser *wg_parser; + IMFStreamDescriptor **descriptors; struct media_stream **streams; ULONG stream_count; + IMFPresentationDescriptor *pres_desc; enum { @@ -1421,9 +1423,11 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) while (source->stream_count--) { struct media_stream *stream = source->streams[source->stream_count]; + IMFStreamDescriptor_Release(source->descriptors[source->stream_count]); IMFMediaEventQueue_Shutdown(stream->event_queue); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } + free(source->descriptors); free(source->streams); MFUnlockWorkQueue(source->async_commands_queue); @@ -1452,7 +1456,6 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { - IMFStreamDescriptor **descriptors = NULL; unsigned int stream_count = UINT_MAX; struct media_source *object; UINT64 total_pres_time = 0; @@ -1514,32 +1517,37 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d stream_count = wg_parser_get_stream_count(parser); - if (!(object->streams = calloc(stream_count, sizeof(*object->streams)))) + if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) + || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) { + free(object->descriptors); hr = E_OUTOFMEMORY; goto fail; } for (i = 0; i < stream_count; ++i) { - if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, i, &object->streams[i]))) - goto fail; + struct media_stream *stream; - if (FAILED(hr = media_stream_init_desc(object->streams[i]))) + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, i, &stream))) + goto fail; + if (FAILED(hr = media_stream_init_desc(stream))) { - ERR("Failed to finish initialization of media stream %p, hr %#lx.\n", object->streams[i], hr); - IMFMediaSource_Release(object->streams[i]->media_source); - IMFMediaEventQueue_Release(object->streams[i]->event_queue); - free(object->streams[i]); + ERR("Failed to finish initialization of media stream %p, hr %#lx.\n", stream, hr); + IMFMediaSource_Release(stream->media_source); + IMFMediaEventQueue_Release(stream->event_queue); + free(stream); goto fail; } + IMFStreamDescriptor_AddRef(stream->descriptor); + object->descriptors[i] = stream->descriptor; + object->streams[i] = stream; object->stream_count++; } /* init presentation descriptor */ - descriptors = malloc(object->stream_count * sizeof(IMFStreamDescriptor *)); for (i = 0; i < object->stream_count; i++) { static const struct @@ -1557,8 +1565,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d DWORD len; char *str; - IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]); - for (j = 0; j < ARRAY_SIZE(tags); ++j) { if (!(str = wg_parser_stream_get_tag(object->streams[i]->wg_stream, tags[j].tag))) @@ -1570,22 +1576,17 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d } strW = malloc(len * sizeof(*strW)); if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) - IMFStreamDescriptor_SetString(descriptors[i], tags[j].mf_attr, strW); + IMFStreamDescriptor_SetString(object->descriptors[i], tags[j].mf_attr, strW); free(strW); free(str); } } - if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc))) + if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, object->descriptors, &object->pres_desc))) goto fail; for (i = 0; i < object->stream_count; i++) - { IMFPresentationDescriptor_SelectStream(object->pres_desc, i); - IMFStreamDescriptor_Release(descriptors[i]); - } - free(descriptors); - descriptors = NULL; for (i = 0; i < object->stream_count; i++) total_pres_time = max(total_pres_time, @@ -1602,18 +1603,13 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d fail: WARN("Failed to construct MFMediaSource, hr %#lx.\n", hr); - if (descriptors) - { - for (i = 0; i < object->stream_count; i++) - IMFStreamDescriptor_Release(descriptors[i]); - free(descriptors); - } - while (object->streams && object->stream_count--) { struct media_stream *stream = object->streams[object->stream_count]; + IMFStreamDescriptor_Release(object->descriptors[object->stream_count]); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } + free(object->descriptors); free(object->streams); if (stream_count != UINT_MAX) From 05a98d752d0acc321d2700c6e39820f2683ed327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 12:08:21 +0200 Subject: [PATCH 359/758] winegstreamer: Create media source presentation descriptor as needed. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index db92219182a..f25af8962fd 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -91,12 +91,12 @@ struct media_source CRITICAL_SECTION cs; struct wg_parser *wg_parser; + UINT64 duration; IMFStreamDescriptor **descriptors; struct media_stream **streams; ULONG stream_count; - IMFPresentationDescriptor *pres_desc; enum { SOURCE_OPENING, @@ -367,7 +367,7 @@ static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, GUID *format, PROPVARIANT *position) { - BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY; + BOOL starting = source->state == SOURCE_STOPPED, seek_message = !starting && position->vt != VT_EMPTY; unsigned int i; HRESULT hr; @@ -392,7 +392,7 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe DWORD stream_id; stream = source->streams[i]; - was_active = stream->active; + was_active = !starting && stream->active; IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); sd = stream_descriptor_from_id(descriptor, stream_id, &selected); @@ -879,7 +879,7 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, object->media_source = source; object->stream_id = id; - object->active = FALSE; + object->active = TRUE; object->eos = FALSE; object->wg_stream = wg_parser_get_stream(wg_parser, id); @@ -1229,7 +1229,6 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) if (!ref) { IMFMediaSource_Shutdown(iface); - IMFPresentationDescriptor_Release(source->pres_desc); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); wg_parser_destroy(source->wg_parser); @@ -1301,6 +1300,7 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * { struct media_source *source = impl_from_IMFMediaSource(iface); HRESULT hr; + UINT i; TRACE("%p, %p.\n", iface, descriptor); @@ -1308,8 +1308,21 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * if (source->state == SOURCE_SHUTDOWN) hr = MF_E_SHUTDOWN; - else - hr = IMFPresentationDescriptor_Clone(source->pres_desc, descriptor); + else if (SUCCEEDED(hr = MFCreatePresentationDescriptor(source->stream_count, source->descriptors, descriptor))) + { + if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_DURATION, source->duration))) + WARN("Failed to set presentation descriptor MF_PD_DURATION, hr %#lx\n", hr); + + for (i = 0; i < source->stream_count; ++i) + { + if (!source->streams[i]->active) + continue; + if (FAILED(hr = IMFPresentationDescriptor_SelectStream(*descriptor, i))) + WARN("Failed to select stream %u, hr %#lx\n", i, hr); + } + + hr = S_OK; + } LeaveCriticalSection(&source->cs); @@ -1458,7 +1471,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d { unsigned int stream_count = UINT_MAX; struct media_source *object; - UINT64 total_pres_time = 0; struct wg_parser *parser; DWORD bytestream_caps; uint64_t file_size; @@ -1540,6 +1552,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d goto fail; } + object->duration = max(object->duration, wg_parser_stream_get_duration(stream->wg_stream)); IMFStreamDescriptor_AddRef(stream->descriptor); object->descriptors[i] = stream->descriptor; object->streams[i] = stream; @@ -1582,19 +1595,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d } } - if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, object->descriptors, &object->pres_desc))) - goto fail; - - for (i = 0; i < object->stream_count; i++) - IMFPresentationDescriptor_SelectStream(object->pres_desc, i); - - for (i = 0; i < object->stream_count; i++) - total_pres_time = max(total_pres_time, - wg_parser_stream_get_duration(object->streams[i]->wg_stream)); - - if (object->stream_count) - IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time); - object->state = SOURCE_STOPPED; *out = &object->IMFMediaSource_iface; From 58a10262b034c0826f8e245b2616f33781fba4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 14:01:57 +0200 Subject: [PATCH 360/758] winegstreamer: Iterate presentation descriptor before starting streams. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 92 +++++++++++++++---------------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index f25af8962fd..6c18b44fd3e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -269,30 +269,6 @@ static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, return hr; } -static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected) -{ - ULONG sd_count; - IMFStreamDescriptor *ret; - unsigned int i; - - if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count))) - return NULL; - - for (i = 0; i < sd_count; i++) - { - DWORD stream_id; - - if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret))) - return NULL; - - if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id) - return ret; - - IMFStreamDescriptor_Release(ret); - } - return NULL; -} - static BOOL enqueue_token(struct media_stream *stream, IUnknown *token) { if (stream->token_queue_count == stream->token_queue_cap) @@ -368,7 +344,8 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe GUID *format, PROPVARIANT *position) { BOOL starting = source->state == SOURCE_STOPPED, seek_message = !starting && position->vt != VT_EMPTY; - unsigned int i; + IMFStreamDescriptor **descriptors; + DWORD i, count; HRESULT hr; TRACE("source %p, descriptor %p, format %s, position %s\n", source, descriptor, @@ -384,29 +361,47 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe position->hVal.QuadPart = 0; } - for (i = 0; i < source->stream_count; i++) - { - struct media_stream *stream; - BOOL was_active, selected; - IMFStreamDescriptor *sd; - DWORD stream_id; + if (!(descriptors = calloc(source->stream_count, sizeof(*descriptors)))) + return E_OUTOFMEMORY; - stream = source->streams[i]; - was_active = !starting && stream->active; + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(descriptor, &count))) + WARN("Failed to get presentation descriptor stream count, hr %#lx\n", hr); - IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); - sd = stream_descriptor_from_id(descriptor, stream_id, &selected); - IMFStreamDescriptor_Release(sd); + for (i = 0; i < count; i++) + { + IMFStreamDescriptor *stream_descriptor; + BOOL selected; + DWORD id; + + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, &selected, &stream_descriptor))) + WARN("Failed to get presentation stream descriptor, hr %#lx\n", hr); + else if (!selected || FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream_descriptor, &id))) + IMFStreamDescriptor_Release(stream_descriptor); + else + descriptors[id] = stream_descriptor; + } + + source->state = SOURCE_RUNNING; + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + BOOL was_active = !starting && stream->active; if (position->vt != VT_EMPTY) stream->eos = FALSE; - if (!(stream->active = selected)) + if (!(stream->active = !!descriptors[i])) wg_parser_stream_disable(stream->wg_stream); - else if (FAILED(hr = media_stream_start(stream, was_active, seek_message, position))) - WARN("Failed to start media stream, hr %#lx\n", hr); + else + { + if (FAILED(hr = media_stream_start(stream, was_active, seek_message, position))) + WARN("Failed to start media stream, hr %#lx\n", hr); + IMFStreamDescriptor_Release(descriptors[i]); + } } + free(descriptors); + source->state = SOURCE_RUNNING; if (position->vt == VT_I8) @@ -854,14 +849,13 @@ static const IMFMediaStreamVtbl media_stream_vtbl = media_stream_RequestSample }; -static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, +static HRESULT media_stream_create(IMFMediaSource *source, struct wg_parser_stream *wg_stream, struct media_stream **out) { - struct wg_parser *wg_parser = impl_from_IMFMediaSource(source)->wg_parser; struct media_stream *object; HRESULT hr; - TRACE("source %p, id %lu.\n", source, id); + TRACE("source %p, wg_stream %p.\n", source, wg_stream); if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; @@ -877,11 +871,10 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, IMFMediaSource_AddRef(source); object->media_source = source; - object->stream_id = id; object->active = TRUE; object->eos = FALSE; - object->wg_stream = wg_parser_get_stream(wg_parser, id); + object->wg_stream = wg_stream; TRACE("Created stream object %p.\n", object); @@ -889,7 +882,7 @@ static HRESULT media_stream_create(IMFMediaSource *source, DWORD id, return S_OK; } -static HRESULT media_stream_init_desc(struct media_stream *stream) +static HRESULT media_stream_init_desc(struct media_stream *stream, UINT id) { IMFMediaTypeHandler *type_handler = NULL; IMFMediaType *stream_types[6]; @@ -982,7 +975,7 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) return E_FAIL; } - if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor))) + if (FAILED(hr = MFCreateStreamDescriptor(id, type_count, stream_types, &stream->descriptor))) goto done; if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) @@ -1539,11 +1532,12 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d for (i = 0; i < stream_count; ++i) { + struct wg_parser_stream *wg_stream = wg_parser_get_stream(parser, i); struct media_stream *stream; - if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, i, &stream))) + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, wg_stream, &stream))) goto fail; - if (FAILED(hr = media_stream_init_desc(stream))) + if (FAILED(hr = media_stream_init_desc(stream, i))) { ERR("Failed to finish initialization of media stream %p, hr %#lx.\n", stream, hr); IMFMediaSource_Release(stream->media_source); @@ -1552,7 +1546,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d goto fail; } - object->duration = max(object->duration, wg_parser_stream_get_duration(stream->wg_stream)); + object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream)); IMFStreamDescriptor_AddRef(stream->descriptor); object->descriptors[i] = stream->descriptor; object->streams[i] = stream; From b301d75dfafd0176f222fbb08839f978a6eba442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:54:55 +0200 Subject: [PATCH 361/758] winegstreamer: Introduce new stream_descriptor_set_tag helper. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 82 +++++++++++++++++-------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 6c18b44fd3e..3501325dcc5 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -269,6 +269,32 @@ static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, return hr; } +static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, struct wg_parser_stream *stream, + const GUID *attr, enum wg_parser_tag tag) +{ + WCHAR *strW; + HRESULT hr; + DWORD len; + char *str; + + if (!(str = wg_parser_stream_get_tag(stream, tag)) + || !(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) + hr = S_OK; + else if (!(strW = malloc(len * sizeof(*strW)))) + hr = E_OUTOFMEMORY; + else + { + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) + hr = IMFStreamDescriptor_SetString(descriptor, attr, strW); + else + hr = E_FAIL; + free(strW); + } + + free(str); + return hr; +} + static BOOL enqueue_token(struct media_stream *stream, IUnknown *token) { if (stream->token_queue_count == stream->token_queue_cap) @@ -1460,6 +1486,25 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; +static void media_source_init_descriptors(struct media_source *source) +{ + HRESULT hr = S_OK; + UINT i; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + IMFStreamDescriptor *descriptor = stream->descriptor; + + if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) + WARN("Failed to set stream descriptor language, hr %#lx\n", hr); + if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + &MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME))) + WARN("Failed to set stream descriptor name, hr %#lx\n", hr); + } +} + HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { unsigned int stream_count = UINT_MAX; @@ -1553,42 +1598,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d object->stream_count++; } - /* init presentation descriptor */ - - for (i = 0; i < object->stream_count; i++) - { - static const struct - { - enum wg_parser_tag tag; - const GUID *mf_attr; - } - tags[] = - { - {WG_PARSER_TAG_LANGUAGE, &MF_SD_LANGUAGE}, - {WG_PARSER_TAG_NAME, &MF_SD_STREAM_NAME}, - }; - unsigned int j; - WCHAR *strW; - DWORD len; - char *str; - - for (j = 0; j < ARRAY_SIZE(tags); ++j) - { - if (!(str = wg_parser_stream_get_tag(object->streams[i]->wg_stream, tags[j].tag))) - continue; - if (!(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) - { - free(str); - continue; - } - strW = malloc(len * sizeof(*strW)); - if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) - IMFStreamDescriptor_SetString(object->descriptors[i], tags[j].mf_attr, strW); - free(strW); - free(str); - } - } - + media_source_init_descriptors(object); object->state = SOURCE_STOPPED; *out = &object->IMFMediaSource_iface; From e86b84d32edab7e24798d0e6a3bc5cbaa0743c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 16 Apr 2023 11:29:30 +0200 Subject: [PATCH 362/758] winegstreamer: Expose a single audio media type from the media source. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 3501325dcc5..3cbbc109480 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -963,30 +963,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream, UINT id) goto done; } } - else if (format.major_type == WG_MAJOR_TYPE_AUDIO) - { - /* Expose at least one PCM and one floating point type for the - consumer to pick from. */ - static const enum wg_audio_format audio_types[] = - { - WG_AUDIO_FORMAT_S16LE, - WG_AUDIO_FORMAT_F32LE, - }; - - if ((stream_types[0] = mf_media_type_from_wg_format(&format))) - type_count = 1; - - for (i = 0; i < ARRAY_SIZE(audio_types); i++) - { - struct wg_format new_format; - if (format.u.audio.format == audio_types[i]) - continue; - new_format = format; - new_format.u.audio.format = audio_types[i]; - if ((stream_types[type_count] = mf_media_type_from_wg_format(&new_format))) - type_count++; - } - } else { if ((stream_types[0] = mf_media_type_from_wg_format(&format))) From 1f63d52bc535989f5fe7856f99b7030e3901ef81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 13:44:45 +0200 Subject: [PATCH 363/758] winegstreamer: Expose a single video media type from the media source. CW-Bug-Id: #21953 --- dlls/mfmediaengine/tests/mfmediaengine.c | 2 +- dlls/winegstreamer/media_source.c | 52 +++--------------------- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 3a5b2bf8253..1a23de5ec86 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -146,7 +146,7 @@ static void check_rgb32_data_(int line, const WCHAR *filename, const BYTE *data, expect_data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); diff = compare_rgb32(data, &length, rect, expect_data); - ok_(__FILE__, line)(diff == 0, "Unexpected %lu%% diff\n", diff); + ok_(__FILE__, line)(diff <= 3 /* small difference in wine */, "Unexpected %lu%% diff\n", diff); } static void init_functions(void) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 3cbbc109480..ccc122343f8 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -919,56 +919,14 @@ static HRESULT media_stream_init_desc(struct media_stream *stream, UINT id) wg_parser_stream_get_preferred_format(stream->wg_stream, &format); - if (format.major_type == WG_MAJOR_TYPE_VIDEO) - { - /* These are the most common native output types of decoders: - https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */ - static const GUID *const video_types[] = - { - &MFVideoFormat_NV12, - &MFVideoFormat_YV12, - &MFVideoFormat_YUY2, - &MFVideoFormat_IYUV, - &MFVideoFormat_I420, - }; - - IMFMediaType *base_type = mf_media_type_from_wg_format(&format); - GUID base_subtype; + /* native exposes NV12 video format before I420 */ + if (format.major_type == WG_MAJOR_TYPE_VIDEO + && format.u.video.format == WG_VIDEO_FORMAT_I420) + format.u.video.format = WG_VIDEO_FORMAT_NV12; - if (!base_type) - { - hr = MF_E_INVALIDMEDIATYPE; - goto done; - } - - IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype); - - stream_types[0] = base_type; + if ((stream_types[0] = mf_media_type_from_wg_format(&format))) type_count = 1; - for (i = 0; i < ARRAY_SIZE(video_types); i++) - { - IMFMediaType *new_type; - - if (IsEqualGUID(&base_subtype, video_types[i])) - continue; - - if (FAILED(hr = MFCreateMediaType(&new_type))) - goto done; - stream_types[type_count++] = new_type; - - if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type))) - goto done; - if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i]))) - goto done; - } - } - else - { - if ((stream_types[0] = mf_media_type_from_wg_format(&format))) - type_count = 1; - } - assert(type_count <= ARRAY_SIZE(stream_types)); if (!type_count) From faa2552d33e20ac66b98a0d958d5d8473be12ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 18:11:18 +0200 Subject: [PATCH 364/758] winegstreamer: Introduce new stream_descriptor_create helper. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 101 +++++++++++++----------------- 1 file changed, 42 insertions(+), 59 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ccc122343f8..eaef1ae791c 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -269,6 +269,37 @@ static HRESULT wg_format_from_stream_descriptor(IMFStreamDescriptor *descriptor, return hr; } +static HRESULT stream_descriptor_create(UINT32 id, struct wg_format *format, IMFStreamDescriptor **out) +{ + IMFStreamDescriptor *descriptor; + IMFMediaTypeHandler *handler; + IMFMediaType *type; + HRESULT hr; + + /* native exposes NV12 video format before I420 */ + if (format->major_type == WG_MAJOR_TYPE_VIDEO + && format->u.video.format == WG_VIDEO_FORMAT_I420) + format->u.video.format = WG_VIDEO_FORMAT_NV12; + + if (!(type = mf_media_type_from_wg_format(format))) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(hr = MFCreateStreamDescriptor(id, 1, &type, &descriptor))) + goto done; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(descriptor, &handler))) + IMFStreamDescriptor_Release(descriptor); + else + { + hr = IMFMediaTypeHandler_SetCurrentMediaType(handler, type); + IMFMediaTypeHandler_Release(handler); + } + +done: + IMFMediaType_Release(type); + *out = SUCCEEDED(hr) ? descriptor : NULL; + return hr; +} + static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, struct wg_parser_stream *stream, const GUID *attr, enum wg_parser_tag tag) { @@ -876,7 +907,7 @@ static const IMFMediaStreamVtbl media_stream_vtbl = }; static HRESULT media_stream_create(IMFMediaSource *source, struct wg_parser_stream *wg_stream, - struct media_stream **out) + IMFStreamDescriptor *descriptor, struct media_stream **out) { struct media_stream *object; HRESULT hr; @@ -897,6 +928,8 @@ static HRESULT media_stream_create(IMFMediaSource *source, struct wg_parser_stre IMFMediaSource_AddRef(source); object->media_source = source; + IMFStreamDescriptor_AddRef(descriptor); + object->descriptor = descriptor; object->active = TRUE; object->eos = FALSE; @@ -908,56 +941,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, struct wg_parser_stre return S_OK; } -static HRESULT media_stream_init_desc(struct media_stream *stream, UINT id) -{ - IMFMediaTypeHandler *type_handler = NULL; - IMFMediaType *stream_types[6]; - struct wg_format format; - DWORD type_count = 0; - HRESULT hr = S_OK; - unsigned int i; - - wg_parser_stream_get_preferred_format(stream->wg_stream, &format); - - /* native exposes NV12 video format before I420 */ - if (format.major_type == WG_MAJOR_TYPE_VIDEO - && format.u.video.format == WG_VIDEO_FORMAT_I420) - format.u.video.format = WG_VIDEO_FORMAT_NV12; - - if ((stream_types[0] = mf_media_type_from_wg_format(&format))) - type_count = 1; - - assert(type_count <= ARRAY_SIZE(stream_types)); - - if (!type_count) - { - ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n"); - return E_FAIL; - } - - if (FAILED(hr = MFCreateStreamDescriptor(id, type_count, stream_types, &stream->descriptor))) - goto done; - - if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) - { - IMFStreamDescriptor_Release(stream->descriptor); - goto done; - } - - if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0]))) - { - IMFStreamDescriptor_Release(stream->descriptor); - goto done; - } - -done: - if (type_handler) - IMFMediaTypeHandler_Release(type_handler); - for (i = 0; i < type_count; i++) - IMFMediaType_Release(stream_types[i]); - return hr; -} - static HRESULT WINAPI media_source_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) { struct media_source *source = impl_from_IMFGetService(iface); @@ -1512,22 +1495,22 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d for (i = 0; i < stream_count; ++i) { struct wg_parser_stream *wg_stream = wg_parser_get_stream(parser, i); + IMFStreamDescriptor *descriptor; struct media_stream *stream; + struct wg_format format; - if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, wg_stream, &stream))) + wg_parser_stream_get_preferred_format(wg_stream, &format); + if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) goto fail; - if (FAILED(hr = media_stream_init_desc(stream, i))) + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, wg_stream, descriptor, &stream))) { - ERR("Failed to finish initialization of media stream %p, hr %#lx.\n", stream, hr); - IMFMediaSource_Release(stream->media_source); - IMFMediaEventQueue_Release(stream->event_queue); - free(stream); + IMFStreamDescriptor_Release(descriptor); goto fail; } object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream)); - IMFStreamDescriptor_AddRef(stream->descriptor); - object->descriptors[i] = stream->descriptor; + IMFStreamDescriptor_AddRef(descriptor); + object->descriptors[i] = descriptor; object->streams[i] = stream; object->stream_count++; } From 13f0a1b92f684b068a9761250077b67f7b2d82ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 16:23:26 +0200 Subject: [PATCH 365/758] winegstreamer: Introduce a new wg_source interface. CW-Bug-Id: #21953 --- MAINTAINERS | 1 + dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 3 + dlls/winegstreamer/main.c | 28 +++++++++ dlls/winegstreamer/media_source.c | 15 ++++- dlls/winegstreamer/unix_private.h | 6 ++ dlls/winegstreamer/unixlib.c | 30 ++++++++++ dlls/winegstreamer/unixlib.h | 11 ++++ dlls/winegstreamer/wg_parser.c | 3 + dlls/winegstreamer/wg_source.c | 99 +++++++++++++++++++++++++++++++ 10 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 dlls/winegstreamer/wg_source.c diff --git a/MAINTAINERS b/MAINTAINERS index 3c4e7a308ba..22a5114e70d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -218,6 +218,7 @@ F: dlls/winegstreamer/h264_decoder.c F: dlls/winegstreamer/resampler.c F: dlls/winegstreamer/video_decoder.c F: dlls/winegstreamer/video_processor.c +F: dlls/winegstreamer/wg_source.c F: dlls/winegstreamer/wg_sample.c F: dlls/winegstreamer/wg_transform.c F: dlls/winegstreamer/wma_decoder.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 56afcea3189..acf4840e1d6 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -25,6 +25,7 @@ C_SRCS = \ wg_format.c \ wg_parser.c \ wg_sample.c \ + wg_source.c \ wg_transform.c \ wm_reader.c \ wma_decoder.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index bb5db1c5f51..1b91ef863a7 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -107,6 +107,9 @@ bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_fo bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); +struct wg_source *wg_source_create(const WCHAR *url, const void *data, uint32_t size); +void wg_source_destroy(struct wg_source *source); + unsigned int wg_format_get_max_size(const struct wg_format *format); HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 232e81a6f27..7c917e2a39d 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -428,6 +428,34 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush) return WINE_UNIX_CALL(unix_wg_transform_drain, ¶ms); } +struct wg_source *wg_source_create(const WCHAR *url, const void *data, uint32_t size) +{ + struct wg_source_create_params params = + { + .data = data, .size = size, + }; + UINT len = url ? WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL) : 0; + char *tmp = url ? malloc(len) : NULL; + + TRACE("url %s, data %p, size %#x\n", debugstr_w(url), data, size); + + if ((params.url = tmp)) + WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL); + + if (!WINE_UNIX_CALL(unix_wg_source_create, ¶ms)) + TRACE("Returning source %p.\n", params.source); + + free(tmp); + return params.source; +} + +void wg_source_destroy(struct wg_source *source) +{ + TRACE("source %p.\n", source); + + WINE_UNIX_CALL(unix_wg_source_destroy, source); +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_ATTACH) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index eaef1ae791c..b31948b3003 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -90,6 +90,7 @@ struct media_source CRITICAL_SECTION cs; + struct wg_source *wg_source; struct wg_parser *wg_parser; UINT64 duration; @@ -1167,6 +1168,7 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) IMFMediaSource_Shutdown(iface); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); + wg_source_destroy(source->wg_source); wg_parser_destroy(source->wg_parser); source->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&source->cs); @@ -1426,6 +1428,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d { unsigned int stream_count = UINT_MAX; struct media_source *object; + struct wg_source *wg_source; struct wg_parser *parser; DWORD bytestream_caps; uint64_t file_size; @@ -1447,8 +1450,14 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d return hr; } + if (!(wg_source = wg_source_create(url, data, size))) + return MF_E_UNSUPPORTED_FORMAT; + if (!(object = calloc(1, sizeof(*object)))) + { + wg_source_destroy(wg_source); return E_OUTOFMEMORY; + } object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; object->IMFGetService_iface.lpVtbl = &media_source_get_service_vtbl; @@ -1456,8 +1465,9 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d object->IMFRateControl_iface.lpVtbl = &media_source_rate_control_vtbl; object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl; object->ref = 1; - object->byte_stream = bytestream; IMFByteStream_AddRef(bytestream); + object->byte_stream = bytestream; + object->wg_source = wg_source; object->rate = 1.0f; InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); @@ -1521,7 +1531,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d *out = &object->IMFMediaSource_iface; return S_OK; - fail: +fail: WARN("Failed to construct MFMediaSource, hr %#lx.\n", hr); while (object->streams && object->stream_count--) @@ -1548,6 +1558,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (object->event_queue) IMFMediaEventQueue_Release(object->event_queue); IMFByteStream_Release(object->byte_stream); + wg_source_destroy(wg_source); free(object); return hr; } diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 1b892cb0a98..9fc071206bc 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -43,6 +43,7 @@ extern GstElement *find_element(GstElementFactoryListType type, GstCaps *src_cap extern bool append_element(GstElement *container, GstElement *element, GstElement **first, GstElement **last) DECLSPEC_HIDDEN; extern bool link_src_to_element(GstPad *src_pad, GstElement *element) DECLSPEC_HIDDEN; extern bool link_element_to_sink(GstElement *element, GstPad *sink_pad) DECLSPEC_HIDDEN; +extern GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) DECLSPEC_HIDDEN; /* wg_format.c */ @@ -60,6 +61,11 @@ extern NTSTATUS wg_transform_read_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; +/* wg_source.c */ + +extern NTSTATUS wg_source_create(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_source_destroy(void *args) DECLSPEC_HIDDEN; + /* wg_allocator.c */ /* wg_allocator_release_sample can be used to release any sample that was requested. */ diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index a185000654d..707a28fed97 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -30,6 +30,10 @@ #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_30 #include +#include +#include +#include +#include #include #include "ntstatus.h" @@ -193,6 +197,32 @@ bool link_element_to_sink(GstElement *element, GstPad *sink_pad) return !ret; } +GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) +{ + const char *extension = url ? strrchr(url, '.') : NULL; + GstTypeFindProbability probability; + GstCaps *caps; + gchar *str; + + if (!(caps = gst_type_find_helper_for_data_with_extension(NULL, data, size, + extension ? extension + 1 : NULL, &probability))) + { + GST_ERROR("Failed to detect caps for url %s, data %p, size %u", url, data, size); + return NULL; + } + + str = gst_caps_to_string(caps); + if (probability > GST_TYPE_FIND_POSSIBLE) + GST_INFO("Detected caps %s with probability %u for url %s, data %p, size %u", + str, probability, url, data, size); + else + GST_FIXME("Detected caps %s with probability %u for url %s, data %p, size %u", + str, probability, url, data, size); + g_free(str); + + return caps; +} + NTSTATUS wg_init_gstreamer(void *arg) { static GstGLContext *gl_context; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 9134cc47413..67e4ce6e831 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -336,6 +336,14 @@ struct wg_transform_drain_params BOOL flush; }; +struct wg_source_create_params +{ + const char *url; + const void *data; + UINT32 size; + struct wg_source *source; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -373,6 +381,9 @@ enum unix_funcs unix_wg_transform_read_data, unix_wg_transform_get_status, unix_wg_transform_drain, + + unix_wg_source_create, + unix_wg_source_destroy, }; #endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 789f81a8a75..84f3c200854 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1894,4 +1894,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_read_data), X(wg_transform_get_status), X(wg_transform_drain), + + X(wg_source_create), + X(wg_source_destroy), }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c new file mode 100644 index 00000000000..cce7e157dba --- /dev/null +++ b/dlls/winegstreamer/wg_source.c @@ -0,0 +1,99 @@ +/* + * Copyright 2023 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winternl.h" +#include "mferror.h" + +#include "unix_private.h" + +struct wg_source +{ + GstElement *container; +}; + +NTSTATUS wg_source_create(void *args) +{ + struct wg_source_create_params *params = args; + struct wg_source *source; + GstCaps *src_caps; + + if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) + return STATUS_UNSUCCESSFUL; + if (!(source = calloc(1, sizeof(*source)))) + { + gst_caps_unref(src_caps); + return STATUS_UNSUCCESSFUL; + } + + if (!(source->container = gst_bin_new("wg_source"))) + goto error; + + gst_element_set_state(source->container, GST_STATE_PAUSED); + if (!gst_element_get_state(source->container, NULL, NULL, -1)) + goto error; + + gst_caps_unref(src_caps); + + params->source = source; + GST_INFO("Created winegstreamer source %p.", source); + return STATUS_SUCCESS; + +error: + if (source->container) + { + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(source->container); + } + free(source); + + gst_caps_unref(src_caps); + + GST_ERROR("Failed to create winegstreamer source."); + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS wg_source_destroy(void *args) +{ + struct wg_source *source = args; + + GST_TRACE("source %p", source); + + gst_element_set_state(source->container, GST_STATE_NULL); + gst_object_unref(source->container); + free(source); + + return STATUS_SUCCESS; +} From f8756d9140f8af916e87ba46c6352b8d301e93f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:06:11 +0200 Subject: [PATCH 366/758] winegstreamer: Create a source pad on the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.c | 15 +++++++++++++++ dlls/winegstreamer/wg_parser.c | 4 +--- dlls/winegstreamer/wg_source.c | 7 +++++++ dlls/winegstreamer/wg_transform.c | 13 ++----------- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 9fc071206bc..2185efbaf91 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -44,6 +44,7 @@ extern bool append_element(GstElement *container, GstElement *element, GstElemen extern bool link_src_to_element(GstPad *src_pad, GstElement *element) DECLSPEC_HIDDEN; extern bool link_element_to_sink(GstElement *element, GstPad *sink_pad) DECLSPEC_HIDDEN; extern GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) DECLSPEC_HIDDEN; +extern GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) DECLSPEC_HIDDEN; /* wg_format.c */ diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 707a28fed97..777c210fa52 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -223,6 +223,21 @@ GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) return caps; } +GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) +{ + GstCaps *pad_caps = caps ? gst_caps_ref(caps) : gst_caps_new_any(); + const char *name = direction == GST_PAD_SRC ? "src" : "sink"; + GstPadTemplate *template; + GstPad *pad; + + if (!pad_caps || !(template = gst_pad_template_new(name, direction, GST_PAD_ALWAYS, pad_caps))) + return NULL; + pad = gst_pad_new_from_template(template, "src"); + g_object_unref(template); + gst_caps_unref(pad_caps); + return pad; +} + NTSTATUS wg_init_gstreamer(void *arg) { static GstGLContext *gl_context; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 84f3c200854..0fe0ae52016 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1438,8 +1438,6 @@ static void query_tags(struct wg_parser_stream *stream) static NTSTATUS wg_parser_connect(void *args) { - GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("quartz_src", - GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); const struct wg_parser_connect_params *params = args; struct wg_parser *parser = params->parser; const WCHAR *uri = params->uri; @@ -1470,7 +1468,7 @@ static NTSTATUS wg_parser_connect(void *args) if (parser->context) gst_element_set_context(parser->container, parser->context); - parser->my_src = gst_pad_new_from_static_template(&src_template, "quartz-src"); + parser->my_src = create_pad_with_caps(GST_PAD_SRC, NULL); gst_pad_set_getrange_function(parser->my_src, src_getrange_cb); gst_pad_set_query_function(parser->my_src, src_query_cb); gst_pad_set_activatemode_function(parser->my_src, src_activate_mode_cb); diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index cce7e157dba..3bda766c5eb 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -41,6 +41,7 @@ struct wg_source { + GstPad *src_pad; GstElement *container; }; @@ -60,6 +61,9 @@ NTSTATUS wg_source_create(void *args) if (!(source->container = gst_bin_new("wg_source"))) goto error; + if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) + goto error; + gst_pad_set_element_private(source->src_pad, source); gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) @@ -77,6 +81,8 @@ NTSTATUS wg_source_create(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); } + if (source->src_pad) + gst_object_unref(source->src_pad); free(source); gst_caps_unref(src_caps); @@ -93,6 +99,7 @@ NTSTATUS wg_source_destroy(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); + gst_object_unref(source->src_pad); free(source); return STATUS_SUCCESS; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index f5fd96ba674..18ba562ab22 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -284,7 +284,6 @@ NTSTATUS wg_transform_create(void *args) GstElement *first = NULL, *last = NULL, *element; GstCaps *raw_caps = NULL, *src_caps = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; - GstPadTemplate *template = NULL; struct wg_transform *transform; const gchar *media_type; GstEvent *event; @@ -310,20 +309,12 @@ NTSTATUS wg_transform_create(void *args) if (!(src_caps = wg_format_to_caps(&input_format))) goto out; - if (!(template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps))) - goto out; - transform->my_src = gst_pad_new_from_template(template, "src"); - g_object_unref(template); - if (!transform->my_src) + if (!(transform->my_src = create_pad_with_caps(GST_PAD_SRC, src_caps))) goto out; if (!(transform->output_caps = wg_format_to_caps(&output_format))) goto out; - if (!(template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, transform->output_caps))) - goto out; - transform->my_sink = gst_pad_new_from_template(template, "sink"); - g_object_unref(template); - if (!transform->my_sink) + if (!(transform->my_sink = create_pad_with_caps(GST_PAD_SINK, transform->output_caps))) goto out; gst_pad_set_element_private(transform->my_sink, transform); From b1e799ec997bd7c64120d84a6bff815af5c801f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 15:22:44 +0200 Subject: [PATCH 367/758] winegstreamer: Create a demuxer element in wg_source_create. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 3bda766c5eb..17147afdec6 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -48,8 +48,9 @@ struct wg_source NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; + GstElement *first = NULL, *last = NULL, *element; + GstCaps *src_caps, *any_caps; struct wg_source *source; - GstCaps *src_caps; if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) return STATUS_UNSUCCESSFUL; @@ -65,6 +66,21 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->src_pad, source); + if (!(any_caps = gst_caps_new_any())) + goto error; + if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, any_caps)) + || !append_element(source->container, element, &first, &last)) + { + gst_caps_unref(any_caps); + goto error; + } + gst_caps_unref(any_caps); + + if (!link_src_to_element(source->src_pad, first)) + goto error; + if (!gst_pad_set_active(source->src_pad, true)) + goto error; + gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; From 12a3181b6d0f8195e31e6f1adeedbb7b317208d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 15:22:55 +0200 Subject: [PATCH 368/758] winegstreamer: Push a stream and segment event to the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 12 +++++++++ dlls/winegstreamer/media_source.c | 3 +++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 ++++++ dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_source.c | 42 +++++++++++++++++++++++++++++++ 7 files changed, 68 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 1b91ef863a7..bad1d2506e3 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -109,6 +109,7 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); struct wg_source *wg_source_create(const WCHAR *url, const void *data, uint32_t size); void wg_source_destroy(struct wg_source *source); +HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 7c917e2a39d..43409ef7512 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -456,6 +456,18 @@ void wg_source_destroy(struct wg_source *source) WINE_UNIX_CALL(unix_wg_source_destroy, source); } +HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size) +{ + struct wg_source_push_data_params params = + { + .source = source, + .data = data, + .size = size, + }; + TRACE("source %p, data %p, size %#x\n", source, data, size); + return HRESULT_FROM_NT(WINE_UNIX_CALL(unix_wg_source_push_data, ¶ms)); +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_ATTACH) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index b31948b3003..b04b3c468c9 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1453,6 +1453,9 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (!(wg_source = wg_source_create(url, data, size))) return MF_E_UNSUPPORTED_FORMAT; + if (FAILED(hr = wg_source_push_data(wg_source, data, size))) + WARN("Failed to push initial data, hr %#lx\n", hr); + if (!(object = calloc(1, sizeof(*object)))) { wg_source_destroy(wg_source); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 2185efbaf91..2d7d487b1a2 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -66,6 +66,7 @@ extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_destroy(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_source_push_data(void *args) DECLSPEC_HIDDEN; /* wg_allocator.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 67e4ce6e831..ffd42b61260 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -344,6 +344,13 @@ struct wg_source_create_params struct wg_source *source; }; +struct wg_source_push_data_params +{ + struct wg_source *source; + const void *data; + UINT32 size; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -384,6 +391,7 @@ enum unix_funcs unix_wg_source_create, unix_wg_source_destroy, + unix_wg_source_push_data, }; #endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 0fe0ae52016..e0ba3850eb8 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1895,4 +1895,5 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_create), X(wg_source_destroy), + X(wg_source_push_data), }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 17147afdec6..7b72ab2ebf2 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -43,14 +43,33 @@ struct wg_source { GstPad *src_pad; GstElement *container; + GstSegment segment; + bool valid_segment; }; +static GstEvent *create_stream_start_event(const char *stream_id) +{ + GstStream *stream; + GstEvent *event; + + if (!(stream = gst_stream_new(stream_id, NULL, GST_STREAM_TYPE_UNKNOWN, 0))) + return NULL; + if ((event = gst_event_new_stream_start(stream_id))) + { + gst_event_set_stream(event, stream); + gst_object_unref(stream); + } + + return event; +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; GstElement *first = NULL, *last = NULL, *element; GstCaps *src_caps, *any_caps; struct wg_source *source; + GstEvent *event; if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) return STATUS_UNSUCCESSFUL; @@ -59,6 +78,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(src_caps); return STATUS_UNSUCCESSFUL; } + gst_segment_init(&source->segment, GST_FORMAT_BYTES); if (!(source->container = gst_bin_new("wg_source"))) goto error; @@ -85,6 +105,9 @@ NTSTATUS wg_source_create(void *args) if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; + if (!(event = create_stream_start_event("wg_source")) + || !gst_pad_push_event(source->src_pad, event)) + goto error; gst_caps_unref(src_caps); params->source = source; @@ -120,3 +143,22 @@ NTSTATUS wg_source_destroy(void *args) return STATUS_SUCCESS; } + +NTSTATUS wg_source_push_data(void *args) +{ + struct wg_source_push_data_params *params = args; + struct wg_source *source = params->source; + GstEvent *event; + + GST_TRACE("source %p, data %p, size %#x", source, params->data, params->size); + + if (!source->valid_segment) + { + if (!(event = gst_event_new_segment(&source->segment)) + || !gst_pad_push_event(source->src_pad, event)) + GST_ERROR("Failed to push new segment event"); + source->valid_segment = true; + } + + return STATUS_SUCCESS; +} From e7852d7ffdffa748e595e7661bfbd5f42f6331c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 15:28:21 +0200 Subject: [PATCH 369/758] winegstreamer: Handle GST_QUERY_DURATION on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 7 +++++-- dlls/winegstreamer/media_source.c | 2 +- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_source.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index bad1d2506e3..84f53405f1d 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -107,7 +107,7 @@ bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_fo bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); -struct wg_source *wg_source_create(const WCHAR *url, const void *data, uint32_t size); +struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size); void wg_source_destroy(struct wg_source *source); HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 43409ef7512..6498b312f52 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -428,16 +428,19 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush) return WINE_UNIX_CALL(unix_wg_transform_drain, ¶ms); } -struct wg_source *wg_source_create(const WCHAR *url, const void *data, uint32_t size) +struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, + const void *data, uint32_t size) { struct wg_source_create_params params = { + .file_size = file_size, .data = data, .size = size, }; UINT len = url ? WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL) : 0; char *tmp = url ? malloc(len) : NULL; - TRACE("url %s, data %p, size %#x\n", debugstr_w(url), data, size); + TRACE("url %s, file_size %#I64x, data %p, size %#x\n", debugstr_w(url), + file_size, data, size); if ((params.url = tmp)) WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index b04b3c468c9..bb9dacd75ca 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1450,7 +1450,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d return hr; } - if (!(wg_source = wg_source_create(url, data, size))) + if (!(wg_source = wg_source_create(url, file_size, data, size))) return MF_E_UNSUPPORTED_FORMAT; if (FAILED(hr = wg_source_push_data(wg_source, data, size))) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index ffd42b61260..23f6db0d0e8 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -339,6 +339,7 @@ struct wg_transform_drain_params struct wg_source_create_params { const char *url; + UINT64 file_size; const void *data; UINT32 size; struct wg_source *source; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 7b72ab2ebf2..65f489215ac 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -47,6 +47,32 @@ struct wg_source bool valid_segment; }; +static gboolean src_query_duration(struct wg_source *source, GstQuery *query) +{ + GstFormat format; + + gst_query_parse_duration(query, &format, NULL); + GST_TRACE("source %p, format %s", source, gst_format_get_name(format)); + if (format != GST_FORMAT_BYTES) + return false; + + gst_query_set_duration(query, format, source->segment.stop); + return true; +} + +static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_DURATION: + return src_query_duration(source, query); + default: + return gst_pad_query_default(pad, parent, query); + } +} + static GstEvent *create_stream_start_event(const char *stream_id) { GstStream *stream; @@ -79,12 +105,14 @@ NTSTATUS wg_source_create(void *args) return STATUS_UNSUCCESSFUL; } gst_segment_init(&source->segment, GST_FORMAT_BYTES); + source->segment.stop = params->file_size; if (!(source->container = gst_bin_new("wg_source"))) goto error; if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) goto error; gst_pad_set_element_private(source->src_pad, source); + gst_pad_set_query_function(source->src_pad, src_query_cb); if (!(any_caps = gst_caps_new_any())) goto error; From 0f1357e7b6eb0d2d6d0b927d05e948ef39ed69b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:49:44 +0200 Subject: [PATCH 370/758] winegstreamer: Handle GST_QUERY_SCHEDULING on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 65f489215ac..6e4efd4a39d 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -60,6 +60,14 @@ static gboolean src_query_duration(struct wg_source *source, GstQuery *query) return true; } +static gboolean src_query_scheduling(struct wg_source *source, GstQuery *query) +{ + GST_TRACE("source %p", source); + gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH); + return true; +} + static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_source *source = gst_pad_get_element_private(pad); @@ -68,6 +76,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { case GST_QUERY_DURATION: return src_query_duration(source, query); + case GST_QUERY_SCHEDULING: + return src_query_scheduling(source, query); default: return gst_pad_query_default(pad, parent, query); } From dd18c78f28d407d8f6ccf46a39373c8e13cda2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 18:31:16 +0200 Subject: [PATCH 371/758] winegstreamer: Create and link sink pads in the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 6e4efd4a39d..f30fafb85a2 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -39,12 +39,17 @@ #include "unix_private.h" +#define WG_SOURCE_MAX_STREAMS 32 + struct wg_source { GstPad *src_pad; GstElement *container; GstSegment segment; bool valid_segment; + + GstPad *stream_pads[WG_SOURCE_MAX_STREAMS]; + guint stream_count; }; static gboolean src_query_duration(struct wg_source *source, GstQuery *query) @@ -83,6 +88,14 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) } } +static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + struct wg_soutce *source = gst_pad_get_element_private(pad); + GST_TRACE("source %p, pad %p, buffer %p.", source, pad, buffer); + gst_buffer_unref(buffer); + return GST_FLOW_EOS; +} + static GstEvent *create_stream_start_event(const char *stream_id) { GstStream *stream; @@ -99,6 +112,24 @@ static GstEvent *create_stream_start_event(const char *stream_id) return event; } +static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) +{ + struct wg_source *source = user; + GstPad *sink_pad; + guint index; + + GST_TRACE("source %p, element %p, pad %p.", source, element, pad); + if ((index = source->stream_count++) >= ARRAY_SIZE(source->stream_pads)) + { + GST_FIXME("Not enough sink pads, need %u", source->stream_count); + return; + } + + sink_pad = source->stream_pads[index]; + if (gst_pad_link(pad, sink_pad) < 0 || !gst_pad_set_active(sink_pad, true)) + GST_ERROR("Failed to link new pad to sink pad %p", sink_pad); +} + NTSTATUS wg_source_create(void *args) { struct wg_source_create_params *params = args; @@ -106,6 +137,8 @@ NTSTATUS wg_source_create(void *args) GstCaps *src_caps, *any_caps; struct wg_source *source; GstEvent *event; + GstPad *peer; + guint i; if (!(src_caps = detect_caps_from_data(params->url, params->data, params->size))) return STATUS_UNSUCCESSFUL; @@ -124,6 +157,14 @@ NTSTATUS wg_source_create(void *args) gst_pad_set_element_private(source->src_pad, source); gst_pad_set_query_function(source->src_pad, src_query_cb); + for (i = 0; i < ARRAY_SIZE(source->stream_pads); i++) + { + if (!(source->stream_pads[i] = create_pad_with_caps(GST_PAD_SINK, NULL))) + goto error; + gst_pad_set_element_private(source->stream_pads[i], source); + gst_pad_set_chain_function(source->stream_pads[i], sink_chain_cb); + } + if (!(any_caps = gst_caps_new_any())) goto error; if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODABLE, src_caps, any_caps)) @@ -132,6 +173,7 @@ NTSTATUS wg_source_create(void *args) gst_caps_unref(any_caps); goto error; } + g_signal_connect(element, "pad-added", G_CALLBACK(pad_added_cb), source); gst_caps_unref(any_caps); if (!link_src_to_element(source->src_pad, first)) @@ -139,6 +181,17 @@ NTSTATUS wg_source_create(void *args) if (!gst_pad_set_active(source->src_pad, true)) goto error; + /* try to link the first output pad, some demuxers only have static pads */ + if ((peer = gst_element_get_static_pad(last, "src"))) + { + GstPad *sink_pad = source->stream_pads[0]; + if (gst_pad_link(peer, sink_pad) < 0 || !gst_pad_set_active(sink_pad, true)) + GST_ERROR("Failed to link static source pad %p", peer); + else + source->stream_count++; + gst_object_unref(peer); + } + gst_element_set_state(source->container, GST_STATE_PAUSED); if (!gst_element_get_state(source->container, NULL, NULL, -1)) goto error; @@ -158,6 +211,8 @@ NTSTATUS wg_source_create(void *args) gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); } + for (i = 0; i < ARRAY_SIZE(source->stream_pads) && source->stream_pads[i]; i++) + gst_object_unref(source->stream_pads[i]); if (source->src_pad) gst_object_unref(source->src_pad); free(source); @@ -171,11 +226,14 @@ NTSTATUS wg_source_create(void *args) NTSTATUS wg_source_destroy(void *args) { struct wg_source *source = args; + guint i; GST_TRACE("source %p", source); gst_element_set_state(source->container, GST_STATE_NULL); gst_object_unref(source->container); + for (i = 0; i < ARRAY_SIZE(source->stream_pads); i++) + gst_object_unref(source->stream_pads[i]); gst_object_unref(source->src_pad); free(source); From 34acd3320803f8091fbe4bc1f4dd29e283c213ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Apr 2023 15:28:30 +0200 Subject: [PATCH 372/758] winegstreamer: Push the initial data from to the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.c | 15 +++++++++++++++ dlls/winegstreamer/wg_source.c | 16 ++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 2d7d487b1a2..b15efa1fe51 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -45,6 +45,7 @@ extern bool link_src_to_element(GstPad *src_pad, GstElement *element) DECLSPEC_H extern bool link_element_to_sink(GstElement *element, GstPad *sink_pad) DECLSPEC_HIDDEN; extern GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) DECLSPEC_HIDDEN; extern GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) DECLSPEC_HIDDEN; +extern GstBuffer *create_buffer_from_bytes(const void *data, guint size) DECLSPEC_HIDDEN; /* wg_format.c */ diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 777c210fa52..618897f0632 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -238,6 +238,21 @@ GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) return pad; } +GstBuffer *create_buffer_from_bytes(const void *data, guint size) +{ + GstBuffer *buffer; + + if (!(buffer = gst_buffer_new_and_alloc(size))) + GST_ERROR("Failed to allocate buffer for %#x bytes\n", size); + else + { + gst_buffer_fill(buffer, 0, data, size); + gst_buffer_set_size(buffer, size); + } + + return buffer; +} + NTSTATUS wg_init_gstreamer(void *arg) { static GstGLContext *gl_context; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index f30fafb85a2..2675d22db29 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -244,6 +244,8 @@ NTSTATUS wg_source_push_data(void *args) { struct wg_source_push_data_params *params = args; struct wg_source *source = params->source; + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buffer; GstEvent *event; GST_TRACE("source %p, data %p, size %#x", source, params->data, params->size); @@ -256,5 +258,19 @@ NTSTATUS wg_source_push_data(void *args) source->valid_segment = true; } + if (!(buffer = create_buffer_from_bytes(params->data, params->size))) + { + GST_WARNING("Failed to allocate buffer for data"); + return STATUS_UNSUCCESSFUL; + } + + source->segment.start += params->size; + if ((ret = gst_pad_push(source->src_pad, buffer)) && ret != GST_FLOW_EOS) + { + GST_WARNING("Failed to push data buffer, ret %d", ret); + source->segment.start -= params->size; + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; } From 3cc2c71f6c333846aa64a7a13d5c49181edb7a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 22:44:15 +0200 Subject: [PATCH 373/758] winegstreamer: Push EOS event when segment is complete. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 2675d22db29..9f42c496fcf 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -272,5 +272,12 @@ NTSTATUS wg_source_push_data(void *args) return STATUS_UNSUCCESSFUL; } + if (source->segment.start != source->segment.stop) + return STATUS_SUCCESS; + + if (!(event = gst_event_new_eos()) + || !gst_pad_push_event(source->src_pad, event)) + GST_WARNING("Failed to push EOS event"); + return STATUS_SUCCESS; } From fb7f441ea8adecf13d430fd43f3371ed74d18445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 17:33:07 +0200 Subject: [PATCH 374/758] winegstreamer: Handle GST_EVENT_SEEK on wg_source src pad. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 77 ++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 9f42c496fcf..9076bf2a8d5 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -52,6 +52,67 @@ struct wg_source guint stream_count; }; +static gboolean src_event_seek(struct wg_source *source, GstEvent *event) +{ + guint32 seqnum = gst_event_get_seqnum(event); + GstSeekType cur_type, stop_type; + GstSeekFlags flags; + GstFormat format; + gint64 cur, stop; + gdouble rate; + + gst_event_parse_seek(event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); + gst_event_unref(event); + if (format != GST_FORMAT_BYTES) + return false; + + GST_TRACE("source %p, rate %f, format %s, flags %#x, cur_type %u, cur %#" G_GINT64_MODIFIER "x, " + "stop_type %u, stop %#" G_GINT64_MODIFIER "x.", source, rate, gst_format_get_name(format), + flags, cur_type, cur, stop_type, stop); + + if (flags & GST_SEEK_FLAG_FLUSH) + { + if (!(event = gst_event_new_flush_start())) + GST_ERROR("Failed to allocate flush_start event"); + else + { + gst_event_set_seqnum(event, seqnum); + if (!gst_pad_push_event(source->src_pad, event)) + GST_ERROR("Failed to push flush_start event"); + } + } + + source->segment.start = cur; + + if (flags & GST_SEEK_FLAG_FLUSH) + { + if (!(event = gst_event_new_flush_stop(true))) + GST_ERROR("Failed to allocate flush_stop event"); + else + { + gst_event_set_seqnum(event, seqnum); + if (!gst_pad_push_event(source->src_pad, event)) + GST_ERROR("Failed to push flush_stop event"); + } + source->valid_segment = false; + } + + return true; +} + +static gboolean src_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_EVENT_TYPE(event)) + { + case GST_EVENT_SEEK: + return src_event_seek(source, event); + default: + return gst_pad_event_default(pad, parent, event); + } +} + static gboolean src_query_duration(struct wg_source *source, GstQuery *query) { GstFormat format; @@ -73,6 +134,19 @@ static gboolean src_query_scheduling(struct wg_source *source, GstQuery *query) return true; } +static gboolean src_query_seeking(struct wg_source *source, GstQuery *query) +{ + GstFormat format; + + gst_query_parse_seeking(query, &format, NULL, NULL, NULL); + GST_TRACE("source %p, format %s", source, gst_format_get_name(format)); + if (format != GST_FORMAT_BYTES) + return false; + + gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, source->segment.stop); + return true; +} + static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_source *source = gst_pad_get_element_private(pad); @@ -83,6 +157,8 @@ static gboolean src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) return src_query_duration(source, query); case GST_QUERY_SCHEDULING: return src_query_scheduling(source, query); + case GST_QUERY_SEEKING: + return src_query_seeking(source, query); default: return gst_pad_query_default(pad, parent, query); } @@ -156,6 +232,7 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->src_pad, source); gst_pad_set_query_function(source->src_pad, src_query_cb); + gst_pad_set_event_function(source->src_pad, src_event_cb); for (i = 0; i < ARRAY_SIZE(source->stream_pads); i++) { From eecd5f5430905a34bcf117d851c7d9fd8a8ae1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 11:53:27 +0200 Subject: [PATCH 375/758] winegstreamer: Handle GST_EVENT_STREAM_START and create wg_source streams. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 9076bf2a8d5..ae631d32259 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -172,6 +172,38 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu return GST_FLOW_EOS; } +static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + guint group, flags; + GstStream *stream; + const gchar *id; + + gst_event_parse_stream_start(event, &id); + gst_event_parse_stream(event, &stream); + gst_event_parse_stream_flags(event, &flags); + if (!gst_event_parse_group_id(event, &group)) + group = -1; + + GST_TRACE("source %p, pad %p, stream %p, id %s, flags %#x, group %d, duration %" GST_TIME_FORMAT, + source, pad, stream, id, flags, group, GST_TIME_ARGS(duration)); + + gst_event_unref(event); + return true; +} + +static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct wg_source *source = gst_pad_get_element_private(pad); + + switch (GST_EVENT_TYPE(event)) + { + case GST_EVENT_STREAM_START: + return sink_event_stream_start(source, pad, event); + default: + return gst_pad_event_default(pad, parent, event); + } +} + static GstEvent *create_stream_start_event(const char *stream_id) { GstStream *stream; @@ -191,7 +223,10 @@ static GstEvent *create_stream_start_event(const char *stream_id) static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) { struct wg_source *source = user; + char stream_id[256]; + GstFlowReturn ret; GstPad *sink_pad; + GstEvent *event; guint index; GST_TRACE("source %p, element %p, pad %p.", source, element, pad); @@ -204,6 +239,18 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) sink_pad = source->stream_pads[index]; if (gst_pad_link(pad, sink_pad) < 0 || !gst_pad_set_active(sink_pad, true)) GST_ERROR("Failed to link new pad to sink pad %p", sink_pad); + + snprintf(stream_id, ARRAY_SIZE(stream_id), "wg_source/%03u", index); + if (!(event = create_stream_start_event(stream_id))) + GST_ERROR("Failed to create stream event for sink pad %p", sink_pad); + else + { + if ((ret = gst_pad_store_sticky_event(pad, event)) < 0) + GST_ERROR("Failed to create pad %p stream, ret %d", sink_pad, ret); + if ((ret = gst_pad_store_sticky_event(sink_pad, event)) < 0) + GST_ERROR("Failed to create pad %p stream, ret %d", sink_pad, ret); + gst_event_unref(event); + } } NTSTATUS wg_source_create(void *args) @@ -240,6 +287,7 @@ NTSTATUS wg_source_create(void *args) goto error; gst_pad_set_element_private(source->stream_pads[i], source); gst_pad_set_chain_function(source->stream_pads[i], sink_chain_cb); + gst_pad_set_event_function(source->stream_pads[i], sink_event_cb); } if (!(any_caps = gst_caps_new_any())) From 7d840624829bd4ed02752e6e8aadab9a4aedd426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 15 Mar 2023 11:47:15 +0100 Subject: [PATCH 376/758] winegstreamer: Query stream duration from GST_EVENT_STREAM_START. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index ae631d32259..e39d44da1b5 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -48,6 +48,7 @@ struct wg_source GstSegment segment; bool valid_segment; + guint64 max_duration; GstPad *stream_pads[WG_SOURCE_MAX_STREAMS]; guint stream_count; }; @@ -176,6 +177,7 @@ static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, G { guint group, flags; GstStream *stream; + gint64 duration; const gchar *id; gst_event_parse_stream_start(event, &id); @@ -183,6 +185,8 @@ static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, G gst_event_parse_stream_flags(event, &flags); if (!gst_event_parse_group_id(event, &group)) group = -1; + if (gst_pad_peer_query_duration(pad, GST_FORMAT_TIME, &duration) && GST_CLOCK_TIME_IS_VALID(duration)) + source->max_duration = max(source->max_duration, duration); GST_TRACE("source %p, pad %p, stream %p, id %s, flags %#x, group %d, duration %" GST_TIME_FORMAT, source, pad, stream, id, flags, group, GST_TIME_ARGS(duration)); From eaabe03f8a485bf62ca27f0c6260458b8414c1bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 12:20:15 +0200 Subject: [PATCH 377/758] winegstreamer: Handle GST_EVENT_CAPS and update wg_source streams caps. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index e39d44da1b5..cace89c1444 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -173,6 +173,28 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu return GST_FLOW_EOS; } +static gboolean sink_event_caps(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + GstStream *stream; + GstCaps *caps; + gchar *str; + + gst_event_parse_caps(event, &caps); + str = gst_caps_to_string(caps); + GST_TRACE("source %p, pad %p, caps %s", source, pad, str); + g_free(str); + + if ((stream = gst_pad_get_stream(pad))) + { + gst_stream_set_caps(stream, gst_caps_copy(caps)); + gst_stream_set_stream_type(stream, stream_type_from_caps(caps)); + gst_object_unref(stream); + } + + gst_event_unref(event); + return !!stream; +} + static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, GstEvent *event) { guint group, flags; @@ -201,6 +223,8 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_CAPS: + return sink_event_caps(source, pad, event); case GST_EVENT_STREAM_START: return sink_event_stream_start(source, pad, event); default: From d59b5995343428ccc0d2725d1d7cd4d23ec6d82e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:49:44 +0200 Subject: [PATCH 378/758] winegstreamer: Introduce a new wg_source_get_status unix call. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 19 ++++++++++++++++ dlls/winegstreamer/media_source.c | 6 ++++-- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 7 ++++++ dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_source.c | 36 +++++++++++++++++++++++++++++++ 7 files changed, 69 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 84f53405f1d..5661d9e3717 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -109,6 +109,7 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size); void wg_source_destroy(struct wg_source *source); +bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count); HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 6498b312f52..d5fe2455932 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -459,6 +459,25 @@ void wg_source_destroy(struct wg_source *source) WINE_UNIX_CALL(unix_wg_source_destroy, source); } +bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count) +{ + struct wg_source_get_status_params params = + { + .source = source, + }; + NTSTATUS status; + + TRACE("source %p, stream_count %p\n", source, stream_count); + + if ((status = WINE_UNIX_CALL(unix_wg_source_get_status, ¶ms)) + && status != STATUS_PENDING) + return false; + + *stream_count = params.stream_count; + TRACE("source %p, stream_count %u\n", source, *stream_count); + return true; +} + HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size) { struct wg_source_push_data_params params = diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index bb9dacd75ca..33a8b86d2a3 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1426,7 +1426,7 @@ static void media_source_init_descriptors(struct media_source *source) HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { - unsigned int stream_count = UINT_MAX; + UINT32 stream_count; struct media_source *object; struct wg_source *wg_source; struct wg_parser *parser; @@ -1455,6 +1455,8 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (FAILED(hr = wg_source_push_data(wg_source, data, size))) WARN("Failed to push initial data, hr %#lx\n", hr); + if (wg_source_get_status(wg_source, &stream_count)) + TRACE("Found %u streams\n", stream_count); if (!(object = calloc(1, sizeof(*object)))) { @@ -1546,7 +1548,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d free(object->descriptors); free(object->streams); - if (stream_count != UINT_MAX) + if (object->state == SOURCE_OPENING) wg_parser_disconnect(object->wg_parser); if (object->read_thread) { diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index b15efa1fe51..774df66c247 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -67,6 +67,7 @@ extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_destroy(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_source_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_push_data(void *args) DECLSPEC_HIDDEN; /* wg_allocator.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 23f6db0d0e8..1e30b9a991a 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -345,6 +345,12 @@ struct wg_source_create_params struct wg_source *source; }; +struct wg_source_get_status_params +{ + struct wg_source *source; + UINT32 stream_count; +}; + struct wg_source_push_data_params { struct wg_source *source; @@ -392,6 +398,7 @@ enum unix_funcs unix_wg_source_create, unix_wg_source_destroy, + unix_wg_source_get_status, unix_wg_source_push_data, }; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index e0ba3850eb8..8c433caa290 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1895,5 +1895,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_create), X(wg_source_destroy), + X(wg_source_get_status), X(wg_source_push_data), }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index cace89c1444..7ad30bd5429 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -53,6 +53,22 @@ struct wg_source guint stream_count; }; +static GstStream *source_get_stream(struct wg_source *source, guint index) +{ + return index >= source->stream_count ? NULL : gst_pad_get_stream(source->stream_pads[index]); +} + +static GstCaps *source_get_stream_caps(struct wg_source *source, guint index) +{ + GstStream *stream; + GstCaps *caps; + if (!(stream = source_get_stream(source, index))) + return NULL; + caps = gst_stream_get_caps(stream); + gst_object_unref(stream); + return caps; +} + static gboolean src_event_seek(struct wg_source *source, GstEvent *event) { guint32 seqnum = gst_event_get_seqnum(event); @@ -393,6 +409,26 @@ NTSTATUS wg_source_destroy(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_get_status(void *args) +{ + struct wg_source_get_status_params *params = args; + struct wg_source *source = params->source; + UINT i, stream_count; + GstCaps *caps; + + GST_TRACE("source %p", source); + + for (i = 0, stream_count = source->stream_count; i < stream_count; i++) + { + if (!(caps = source_get_stream_caps(source, i))) + return STATUS_PENDING; + gst_caps_unref(caps); + } + + params->stream_count = stream_count; + return STATUS_SUCCESS; +} + NTSTATUS wg_source_push_data(void *args) { struct wg_source_push_data_params *params = args; From b0d86f7ead7fe7a0c5eb8cf27d13c53bdc80fceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 16:27:15 +0200 Subject: [PATCH 379/758] winegstreamer: Set the MF_PD_TOTAL_FILE_SIZE presentation descriptor attribute. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 33a8b86d2a3..b013703ee58 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -92,6 +92,7 @@ struct media_source struct wg_source *wg_source; struct wg_parser *wg_parser; + UINT64 file_size; UINT64 duration; IMFStreamDescriptor **descriptors; @@ -1248,6 +1249,8 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * hr = MF_E_SHUTDOWN; else if (SUCCEEDED(hr = MFCreatePresentationDescriptor(source->stream_count, source->descriptors, descriptor))) { + if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_TOTAL_FILE_SIZE, source->file_size))) + WARN("Failed to set presentation descriptor MF_PD_TOTAL_FILE_SIZE, hr %#lx\n", hr); if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_DURATION, source->duration))) WARN("Failed to set presentation descriptor MF_PD_DURATION, hr %#lx\n", hr); @@ -1473,6 +1476,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d IMFByteStream_AddRef(bytestream); object->byte_stream = bytestream; object->wg_source = wg_source; + object->file_size = file_size; object->rate = 1.0f; InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); From a9e5b85a680e67569f856d3d6cfd61d5aa243e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 16:44:32 +0200 Subject: [PATCH 380/758] winegstreamer: Read stream data and provide samples to wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 3 ++- dlls/winegstreamer/main.c | 10 +++++++--- dlls/winegstreamer/media_source.c | 23 +++++++++++++++++------ dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_source.c | 1 + 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 5661d9e3717..a0ced0b4035 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -109,7 +109,8 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size); void wg_source_destroy(struct wg_source *source); -bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count); +bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, + uint64_t *read_offset); HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index d5fe2455932..69b8361361a 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -459,7 +459,8 @@ void wg_source_destroy(struct wg_source *source) WINE_UNIX_CALL(unix_wg_source_destroy, source); } -bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count) +bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, + uint64_t *read_offset) { struct wg_source_get_status_params params = { @@ -467,14 +468,17 @@ bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count) }; NTSTATUS status; - TRACE("source %p, stream_count %p\n", source, stream_count); + TRACE("source %p, stream_count %p, read_offset %p\n", + source, stream_count, read_offset); if ((status = WINE_UNIX_CALL(unix_wg_source_get_status, ¶ms)) && status != STATUS_PENDING) return false; *stream_count = params.stream_count; - TRACE("source %p, stream_count %u\n", source, *stream_count); + *read_offset = params.read_offset; + TRACE("source %p, stream_count %u, read_offset %#I64x\n", + source, *stream_count, *read_offset); return true; } diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index b013703ee58..4bc260b1f75 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1429,12 +1429,12 @@ static void media_source_init_descriptors(struct media_source *source) HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { + UINT64 next_offset, file_size; UINT32 stream_count; struct media_source *object; struct wg_source *wg_source; struct wg_parser *parser; - DWORD bytestream_caps; - uint64_t file_size; + DWORD bytestream_caps, read_size = size; unsigned int i; HRESULT hr; @@ -1456,10 +1456,21 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (!(wg_source = wg_source_create(url, file_size, data, size))) return MF_E_UNSUPPORTED_FORMAT; - if (FAILED(hr = wg_source_push_data(wg_source, data, size))) - WARN("Failed to push initial data, hr %#lx\n", hr); - if (wg_source_get_status(wg_source, &stream_count)) - TRACE("Found %u streams\n", stream_count); + while (SUCCEEDED(hr) && SUCCEEDED(hr = wg_source_push_data(wg_source, data, read_size)) + && wg_source_get_status(wg_source, &stream_count, &next_offset) + && !stream_count && (read_size = min(file_size - min(file_size, next_offset), size))) + { + if (FAILED(hr = IMFByteStream_SetCurrentPosition(bytestream, next_offset))) + WARN("Failed to seek stream to %#I64x, hr %#lx\n", next_offset, hr); + else if (FAILED(hr = IMFByteStream_Read(bytestream, data, read_size, &read_size))) + WARN("Failed to read %#lx bytes from stream, hr %#lx\n", read_size, hr); + } + + if (!stream_count) + { + wg_source_destroy(wg_source); + return MF_E_UNSUPPORTED_FORMAT; + } if (!(object = calloc(1, sizeof(*object)))) { diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 1e30b9a991a..be9fe19911d 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -349,6 +349,7 @@ struct wg_source_get_status_params { struct wg_source *source; UINT32 stream_count; + UINT64 read_offset; }; struct wg_source_push_data_params diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 7ad30bd5429..e444eeff7a3 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -426,6 +426,7 @@ NTSTATUS wg_source_get_status(void *args) } params->stream_count = stream_count; + params->read_offset = source->segment.start; return STATUS_SUCCESS; } From 06b64988e6330e2f02bf28d94b1d7128bb34b81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:49:44 +0200 Subject: [PATCH 381/758] winegstreamer: Query the stream max duration from the wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 11 ++++++----- dlls/winegstreamer/media_source.c | 6 +++--- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_source.c | 1 + 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index a0ced0b4035..483ce1cf994 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -110,7 +110,7 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size); void wg_source_destroy(struct wg_source *source); bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, - uint64_t *read_offset); + uint64_t *duration, uint64_t *read_offset); HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 69b8361361a..94d765bfde7 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -460,7 +460,7 @@ void wg_source_destroy(struct wg_source *source) } bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, - uint64_t *read_offset) + uint64_t *duration, uint64_t *read_offset) { struct wg_source_get_status_params params = { @@ -468,17 +468,18 @@ bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, }; NTSTATUS status; - TRACE("source %p, stream_count %p, read_offset %p\n", - source, stream_count, read_offset); + TRACE("source %p, stream_count %p, duration %p, read_offset %p\n", + source, stream_count, duration, read_offset); if ((status = WINE_UNIX_CALL(unix_wg_source_get_status, ¶ms)) && status != STATUS_PENDING) return false; *stream_count = params.stream_count; + *duration = params.duration; *read_offset = params.read_offset; - TRACE("source %p, stream_count %u, read_offset %#I64x\n", - source, *stream_count, *read_offset); + TRACE("source %p, stream_count %u, duration %s, read_offset %#I64x\n", + source, *stream_count, debugstr_time(*duration), *read_offset); return true; } diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 4bc260b1f75..65424150ee7 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1429,7 +1429,7 @@ static void media_source_init_descriptors(struct media_source *source) HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) { - UINT64 next_offset, file_size; + UINT64 duration, next_offset, file_size; UINT32 stream_count; struct media_source *object; struct wg_source *wg_source; @@ -1457,7 +1457,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d return MF_E_UNSUPPORTED_FORMAT; while (SUCCEEDED(hr) && SUCCEEDED(hr = wg_source_push_data(wg_source, data, read_size)) - && wg_source_get_status(wg_source, &stream_count, &next_offset) + && wg_source_get_status(wg_source, &stream_count, &duration, &next_offset) && !stream_count && (read_size = min(file_size - min(file_size, next_offset), size))) { if (FAILED(hr = IMFByteStream_SetCurrentPosition(bytestream, next_offset))) @@ -1488,6 +1488,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d object->byte_stream = bytestream; object->wg_source = wg_source; object->file_size = file_size; + object->duration = duration; object->rate = 1.0f; InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); @@ -1538,7 +1539,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d goto fail; } - object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream)); IMFStreamDescriptor_AddRef(descriptor); object->descriptors[i] = descriptor; object->streams[i] = stream; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index be9fe19911d..9e8d91b3316 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -349,6 +349,7 @@ struct wg_source_get_status_params { struct wg_source *source; UINT32 stream_count; + UINT64 duration; UINT64 read_offset; }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index e444eeff7a3..570c2f19c72 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -426,6 +426,7 @@ NTSTATUS wg_source_get_status(void *args) } params->stream_count = stream_count; + params->duration = source->max_duration / 100; params->read_offset = source->segment.start; return STATUS_SUCCESS; } From d7a312c5abf1183118e972a467c802c25d358eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 26 Apr 2023 18:05:14 +0200 Subject: [PATCH 382/758] winegstreamer: Set the MF_PD_MIME_TYPE presentation descriptor attribute. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 3 ++- dlls/winegstreamer/main.c | 9 ++++++--- dlls/winegstreamer/media_source.c | 7 ++++++- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_source.c | 9 +++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 483ce1cf994..394cb821e58 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -107,7 +107,8 @@ bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_fo bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); -struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size); +struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, + const void *data, uint32_t size, WCHAR mime_type[256]); void wg_source_destroy(struct wg_source *source); bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, uint64_t *duration, uint64_t *read_offset); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 94d765bfde7..a4020746e20 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -429,7 +429,7 @@ HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush) } struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, - const void *data, uint32_t size) + const void *data, uint32_t size, WCHAR mime_type[256]) { struct wg_source_create_params params = { @@ -439,14 +439,17 @@ struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, UINT len = url ? WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, NULL, NULL) : 0; char *tmp = url ? malloc(len) : NULL; - TRACE("url %s, file_size %#I64x, data %p, size %#x\n", debugstr_w(url), - file_size, data, size); + TRACE("url %s, file_size %#I64x, data %p, size %#x, mime_type %p\n", debugstr_w(url), + file_size, data, size, mime_type); if ((params.url = tmp)) WideCharToMultiByte(CP_ACP, 0, url, -1, tmp, len, NULL, NULL); if (!WINE_UNIX_CALL(unix_wg_source_create, ¶ms)) + { + MultiByteToWideChar(CP_ACP, 0, params.mime_type, -1, mime_type, 256); TRACE("Returning source %p.\n", params.source); + } free(tmp); return params.source; diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 65424150ee7..e77033c5cb7 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -92,6 +92,7 @@ struct media_source struct wg_source *wg_source; struct wg_parser *wg_parser; + WCHAR mime_type[256]; UINT64 file_size; UINT64 duration; @@ -1249,6 +1250,8 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * hr = MF_E_SHUTDOWN; else if (SUCCEEDED(hr = MFCreatePresentationDescriptor(source->stream_count, source->descriptors, descriptor))) { + if (FAILED(hr = IMFPresentationDescriptor_SetString(*descriptor, &MF_PD_MIME_TYPE, source->mime_type))) + WARN("Failed to set presentation descriptor MF_PD_MIME_TYPE, hr %#lx\n", hr); if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_TOTAL_FILE_SIZE, source->file_size))) WARN("Failed to set presentation descriptor MF_PD_TOTAL_FILE_SIZE, hr %#lx\n", hr); if (FAILED(hr = IMFPresentationDescriptor_SetUINT64(*descriptor, &MF_PD_DURATION, source->duration))) @@ -1435,6 +1438,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d struct wg_source *wg_source; struct wg_parser *parser; DWORD bytestream_caps, read_size = size; + WCHAR mime_type[256]; unsigned int i; HRESULT hr; @@ -1453,7 +1457,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d return hr; } - if (!(wg_source = wg_source_create(url, file_size, data, size))) + if (!(wg_source = wg_source_create(url, file_size, data, size, mime_type))) return MF_E_UNSUPPORTED_FORMAT; while (SUCCEEDED(hr) && SUCCEEDED(hr = wg_source_push_data(wg_source, data, read_size)) @@ -1487,6 +1491,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d IMFByteStream_AddRef(bytestream); object->byte_stream = bytestream; object->wg_source = wg_source; + wcscpy(object->mime_type, mime_type); object->file_size = file_size; object->duration = duration; object->rate = 1.0f; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 9e8d91b3316..c685529562d 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -342,6 +342,7 @@ struct wg_source_create_params UINT64 file_size; const void *data; UINT32 size; + char mime_type[256]; struct wg_source *source; }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 570c2f19c72..928f0bdb074 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -303,6 +303,7 @@ NTSTATUS wg_source_create(void *args) GstElement *first = NULL, *last = NULL, *element; GstCaps *src_caps, *any_caps; struct wg_source *source; + const gchar *media_type; GstEvent *event; GstPad *peer; guint i; @@ -317,6 +318,14 @@ NTSTATUS wg_source_create(void *args) gst_segment_init(&source->segment, GST_FORMAT_BYTES); source->segment.stop = params->file_size; + media_type = gst_structure_get_name(gst_caps_get_structure(src_caps, 0)); + if (!strcmp(media_type, "video/quicktime")) + strcpy(params->mime_type, "video/mp4"); + else if (!strcmp(media_type, "video/x-msvideo")) + strcpy(params->mime_type, "video/avi"); + else + lstrcpynA(params->mime_type, media_type, ARRAY_SIZE(params->mime_type)); + if (!(source->container = gst_bin_new("wg_source"))) goto error; if (!(source->src_pad = create_pad_with_caps(GST_PAD_SRC, src_caps))) From 04bdb969129cf17cc5da163ba8bba9a0cf303cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 18:35:22 +0200 Subject: [PATCH 383/758] winegstreamer: Handle GST_EVENT_TAG and udpate wg_source streams tags. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 928f0bdb074..ab588bd5016 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -211,6 +211,31 @@ static gboolean sink_event_caps(struct wg_source *source, GstPad *pad, GstEvent return !!stream; } +static gboolean sink_event_tag(struct wg_source *source, GstPad *pad, GstEvent *event) +{ + GstTagList *new_tags; + GstStream *stream; + + gst_event_parse_tag(event, &new_tags); + GST_TRACE("source %p, pad %p, new_tags %p", source, pad, new_tags); + + if ((stream = gst_pad_get_stream(pad))) + { + GstTagList *old_tags = gst_stream_get_tags(stream); + if ((new_tags = gst_tag_list_merge(old_tags, new_tags, GST_TAG_MERGE_REPLACE))) + { + gst_stream_set_tags(stream, new_tags); + gst_tag_list_unref(new_tags); + } + if (old_tags) + gst_tag_list_unref(old_tags); + gst_object_unref(stream); + } + + gst_event_unref(event); + return stream && new_tags; +} + static gboolean sink_event_stream_start(struct wg_source *source, GstPad *pad, GstEvent *event) { guint group, flags; @@ -241,6 +266,8 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) { case GST_EVENT_CAPS: return sink_event_caps(source, pad, event); + case GST_EVENT_TAG: + return sink_event_tag(source, pad, event); case GST_EVENT_STREAM_START: return sink_event_stream_start(source, pad, event); default: From 992c14611860e321162e5fde968bdd85b4faab25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 19:09:23 +0200 Subject: [PATCH 384/758] winegstreamer: Sort media source "video/mp4" streams by stream type. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 ++ dlls/winegstreamer/main.c | 19 ++++++++++++ dlls/winegstreamer/media_source.c | 50 +++++++++++++++++++++++++++++-- dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 8 +++++ dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_source.c | 18 +++++++++++ 7 files changed, 97 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 394cb821e58..b6c94cb7f52 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -113,6 +113,8 @@ void wg_source_destroy(struct wg_source *source); bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, uint64_t *duration, uint64_t *read_offset); HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); +bool wg_source_get_stream_format(struct wg_source *source, UINT32 index, + struct wg_format *format); unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index a4020746e20..bb7bcb414fe 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -498,6 +498,25 @@ HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t return HRESULT_FROM_NT(WINE_UNIX_CALL(unix_wg_source_push_data, ¶ms)); } +bool wg_source_get_stream_format(struct wg_source *source, UINT32 index, + struct wg_format *format) +{ + struct wg_source_get_stream_format_params params = + { + .source = source, + .index = index, + }; + + TRACE("source %p, index %u, format %p\n", source, + index, format); + + if (WINE_UNIX_CALL(unix_wg_source_get_stream_format, ¶ms)) + return false; + + *format = params.format; + return true; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_ATTACH) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e77033c5cb7..4ee7250cb3a 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -99,6 +99,7 @@ struct media_source IMFStreamDescriptor **descriptors; struct media_stream **streams; ULONG stream_count; + UINT *stream_map; enum { @@ -1384,6 +1385,7 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) IMFMediaEventQueue_Shutdown(stream->event_queue); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } + free(source->stream_map); free(source->descriptors); free(source->streams); @@ -1411,6 +1413,46 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, }; +static void media_source_init_stream_map(struct media_source *source, UINT stream_count) +{ + struct wg_format format; + int i, n = 0; + + if (wcscmp(source->mime_type, L"video/mp4")) + { + for (i = stream_count - 1; i >= 0; i--) + { + TRACE("mapping stream %u to wg_source stream %u\n", i, i); + source->stream_map[i] = i; + } + return; + } + + for (i = stream_count - 1; i >= 0; i--) + { + wg_source_get_stream_format(source->wg_source, i, &format); + if (format.major_type == WG_MAJOR_TYPE_UNKNOWN) continue; + if (format.major_type >= WG_MAJOR_TYPE_VIDEO) continue; + TRACE("mapping stream %u to wg_source stream %u\n", n, i); + source->stream_map[n++] = i; + } + for (i = stream_count - 1; i >= 0; i--) + { + wg_source_get_stream_format(source->wg_source, i, &format); + if (format.major_type == WG_MAJOR_TYPE_UNKNOWN) continue; + if (format.major_type < WG_MAJOR_TYPE_VIDEO) continue; + TRACE("mapping stream %u to wg_source stream %u\n", n, i); + source->stream_map[n++] = i; + } + for (i = stream_count - 1; i >= 0; i--) + { + wg_source_get_stream_format(source->wg_source, i, &format); + if (format.major_type != WG_MAJOR_TYPE_UNKNOWN) continue; + TRACE("mapping stream %u to wg_source stream %u\n", n, i); + source->stream_map[n++] = i; + } +} + static void media_source_init_descriptors(struct media_source *source) { HRESULT hr = S_OK; @@ -1518,16 +1560,19 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (FAILED(hr = wg_parser_connect(parser, file_size, NULL))) goto fail; - stream_count = wg_parser_get_stream_count(parser); - if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) + || !(object->stream_map = calloc(stream_count, sizeof(*object->stream_map))) || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) { + free(object->stream_map); free(object->descriptors); hr = E_OUTOFMEMORY; goto fail; } + media_source_init_stream_map(object, stream_count); + + stream_count = wg_parser_get_stream_count(parser); for (i = 0; i < stream_count; ++i) { struct wg_parser_stream *wg_stream = wg_parser_get_stream(parser, i); @@ -1565,6 +1610,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d IMFStreamDescriptor_Release(object->descriptors[object->stream_count]); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } + free(object->stream_map); free(object->descriptors); free(object->streams); diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 774df66c247..293c9c407de 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -69,6 +69,7 @@ extern NTSTATUS wg_source_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_destroy(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_push_data(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_source_get_stream_format(void *args) DECLSPEC_HIDDEN; /* wg_allocator.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index c685529562d..7979beaeedb 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -361,6 +361,13 @@ struct wg_source_push_data_params UINT32 size; }; +struct wg_source_get_stream_format_params +{ + struct wg_source *source; + UINT32 index; + struct wg_format format; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -403,6 +410,7 @@ enum unix_funcs unix_wg_source_destroy, unix_wg_source_get_status, unix_wg_source_push_data, + unix_wg_source_get_stream_format, }; #endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 8c433caa290..f5c26b2bf5c 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1897,4 +1897,5 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_destroy), X(wg_source_get_status), X(wg_source_push_data), + X(wg_source_get_stream_format), }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index ab588bd5016..8b51d8c54cd 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -508,3 +508,21 @@ NTSTATUS wg_source_push_data(void *args) return STATUS_SUCCESS; } + +NTSTATUS wg_source_get_stream_format(void *args) +{ + struct wg_source_get_stream_format_params *params = args; + struct wg_source *source = params->source; + guint index = params->index; + GstCaps *caps; + + GST_TRACE("source %p, index %u", source, index); + + if (!(caps = source_get_stream_caps(source, index))) + return STATUS_UNSUCCESSFUL; + wg_format_from_caps(¶ms->format, caps); + + gst_caps_unref(caps); + return STATUS_SUCCESS; +} + From 0a86fb367edda1aa8dbd87a37fa58296b1ed00d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:14:04 +0200 Subject: [PATCH 385/758] winegstreamer: Initialize media source presentation from wg_source. CW-Bug-Id: #21953 --- dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 27 +++++++ dlls/winegstreamer/media_source.c | 114 +++++++++++++++++++++++------- dlls/winegstreamer/unix_private.h | 6 ++ dlls/winegstreamer/unixlib.c | 50 +++++++++++++ dlls/winegstreamer/unixlib.h | 10 +++ dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_source.c | 70 ++++++++++++++++++ 8 files changed, 254 insertions(+), 26 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index b6c94cb7f52..b31feddd5ef 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -115,6 +115,8 @@ bool wg_source_get_status(struct wg_source *source, uint32_t *stream_count, HRESULT wg_source_push_data(struct wg_source *source, const void *data, uint32_t size); bool wg_source_get_stream_format(struct wg_source *source, UINT32 index, struct wg_format *format); +char *wg_source_get_stream_tag(struct wg_source *source, UINT32 index, + enum wg_parser_tag tag); unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index bb7bcb414fe..6175fc94a01 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -517,6 +517,33 @@ bool wg_source_get_stream_format(struct wg_source *source, UINT32 index, return true; } +char *wg_source_get_stream_tag(struct wg_source *source, UINT32 index, enum wg_parser_tag tag) +{ + struct wg_source_get_stream_tag_params params = + { + .source = source, + .index = index, + .tag = tag, + }; + char *buffer; + + if (WINE_UNIX_CALL(unix_wg_source_get_stream_tag, ¶ms) != STATUS_BUFFER_TOO_SMALL) + return NULL; + if (!(buffer = malloc(params.size))) + { + ERR("No memory.\n"); + return NULL; + } + params.buffer = buffer; + if (WINE_UNIX_CALL(unix_wg_source_get_stream_tag, ¶ms)) + { + ERR("wg_source_get_stream_tag failed unexpectedly.\n"); + free(buffer); + return NULL; + } + return buffer; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_ATTACH) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 4ee7250cb3a..e2a53247588 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -36,8 +36,6 @@ struct media_stream IMFMediaEventQueue *event_queue; IMFStreamDescriptor *descriptor; - struct wg_parser_stream *wg_stream; - IUnknown **token_queue; LONG token_queue_count; LONG token_queue_cap; @@ -304,15 +302,15 @@ static HRESULT stream_descriptor_create(UINT32 id, struct wg_format *format, IMF return hr; } -static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, struct wg_parser_stream *stream, - const GUID *attr, enum wg_parser_tag tag) +static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, + struct wg_source *source, UINT index, const GUID *attr, enum wg_parser_tag tag) { WCHAR *strW; HRESULT hr; DWORD len; char *str; - if (!(str = wg_parser_stream_get_tag(stream, tag)) + if (!(str = wg_source_get_stream_tag(source, index, tag)) || !(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) hr = S_OK; else if (!(strW = malloc(len * sizeof(*strW)))) @@ -330,6 +328,49 @@ static HRESULT stream_descriptor_set_tag(IMFStreamDescriptor *descriptor, struct return hr; } +static HRESULT map_stream_to_wg_parser_stream(struct media_source *source, UINT stream) +{ + struct wg_parser_stream *wg_stream; + struct wg_format stream_format; + HRESULT hr; + UINT i; + + if (!(wg_stream = wg_parser_get_stream(source->wg_parser, stream))) + return E_FAIL; + wg_parser_stream_get_preferred_format(wg_stream, &stream_format); + + for (i = 0; i < source->stream_count; i++) + { + struct wg_format format; + + if (FAILED(hr = wg_format_from_stream_descriptor(source->descriptors[i], &format))) + return hr; + if (stream_format.major_type != format.major_type) + continue; + if (source->stream_map[i]) + continue; + + TRACE("Mapped stream %u with descriptor %u\n", stream, i); + source->stream_map[i] = stream + 1; + return S_OK; + } + + return E_FAIL; +} + +static HRESULT media_stream_get_wg_parser_stream(struct media_stream *stream, struct wg_parser_stream **wg_stream) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + HRESULT hr; + DWORD id; + + if (FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &id))) + return hr; + if (!(id = source->stream_map[id]) || !(*wg_stream = wg_parser_get_stream(source->wg_parser, id - 1))) + return MF_E_INVALIDSTREAMNUMBER; + return S_OK; +} + static BOOL enqueue_token(struct media_stream *stream, IUnknown *token) { if (stream->token_queue_count == stream->token_queue_cap) @@ -385,14 +426,17 @@ static void flush_token_queue(struct media_stream *stream, BOOL send) static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL seeking, const PROPVARIANT *position) { struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct wg_parser_stream *wg_stream; struct wg_format format; HRESULT hr; TRACE("source %p, stream %p\n", source, stream); + if (FAILED(hr = media_stream_get_wg_parser_stream(stream, &wg_stream))) + return hr; if (FAILED(hr = wg_format_from_stream_descriptor(stream->descriptor, &format))) WARN("Failed to get wg_format from stream descriptor, hr %#lx\n", hr); - wg_parser_stream_enable(stream->wg_stream, &format, 0); + wg_parser_stream_enable(wg_stream, &format, 0); if (FAILED(hr = IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, active ? MEUpdatedStream : MENewStream, &GUID_NULL, S_OK, (IUnknown *)&stream->IMFMediaStream_iface))) @@ -447,12 +491,17 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe { struct media_stream *stream = source->streams[i]; BOOL was_active = !starting && stream->active; + struct wg_parser_stream *wg_stream; if (position->vt != VT_EMPTY) stream->eos = FALSE; if (!(stream->active = !!descriptors[i])) - wg_parser_stream_disable(stream->wg_stream); + { + if (FAILED(hr = media_stream_get_wg_parser_stream(stream, &wg_stream))) + return hr; + wg_parser_stream_disable(wg_stream); + } else { if (FAILED(hr = media_stream_start(stream, was_active, seek_message, position))) @@ -466,8 +515,11 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe source->state = SOURCE_RUNNING; if (position->vt == VT_I8) - wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0, position->hVal.QuadPart, 0, + { + struct wg_parser_stream *wg_stream = wg_parser_get_stream(source->wg_parser, 0); + wg_parser_stream_seek(wg_stream, 1.0, position->hVal.QuadPart, 0, AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning); + } for (i = 0; i < source->stream_count; i++) flush_token_queue(source->streams[i], position->vt == VT_EMPTY); @@ -524,7 +576,8 @@ static HRESULT media_source_stop(struct media_source *source) return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); } -static HRESULT media_stream_send_sample(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) +static HRESULT media_stream_send_sample(struct media_stream *stream, struct wg_parser_stream *wg_stream, + const struct wg_parser_buffer *wg_buffer, IUnknown *token) { IMFSample *sample = NULL; IMFMediaBuffer *buffer; @@ -538,13 +591,13 @@ static HRESULT media_stream_send_sample(struct media_stream *stream, const struc if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) goto out; - if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) + if (!wg_parser_stream_copy_buffer(wg_stream, data, 0, wg_buffer->size)) { - wg_parser_stream_release_buffer(stream->wg_stream); + wg_parser_stream_release_buffer(wg_stream); IMFMediaBuffer_Unlock(buffer); goto out; } - wg_parser_stream_release_buffer(stream->wg_stream); + wg_parser_stream_release_buffer(wg_stream); if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) goto out; @@ -597,12 +650,16 @@ static HRESULT media_stream_send_eos(struct media_source *source, struct media_s static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) { struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct wg_parser_stream *wg_stream; struct wg_parser_buffer buffer; + HRESULT hr; TRACE("%p, %p\n", stream, token); - if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) - return media_stream_send_sample(stream, &buffer, token); + if (FAILED(hr = media_stream_get_wg_parser_stream(stream, &wg_stream))) + return hr; + if (wg_parser_stream_get_buffer(source->wg_parser, wg_stream, &buffer)) + return media_stream_send_sample(stream, wg_stream, &buffer, token); return media_stream_send_eos(source, stream); } @@ -910,13 +967,13 @@ static const IMFMediaStreamVtbl media_stream_vtbl = media_stream_RequestSample }; -static HRESULT media_stream_create(IMFMediaSource *source, struct wg_parser_stream *wg_stream, - IMFStreamDescriptor *descriptor, struct media_stream **out) +static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor *descriptor, + struct media_stream **out) { struct media_stream *object; HRESULT hr; - TRACE("source %p, wg_stream %p.\n", source, wg_stream); + TRACE("source %p, descriptor %p.\n", source, descriptor); if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; @@ -937,7 +994,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, struct wg_parser_stre object->active = TRUE; object->eos = FALSE; - object->wg_stream = wg_stream; TRACE("Created stream object %p.\n", object); @@ -1455,21 +1511,28 @@ static void media_source_init_stream_map(struct media_source *source, UINT strea static void media_source_init_descriptors(struct media_source *source) { - HRESULT hr = S_OK; + HRESULT hr; UINT i; for (i = 0; i < source->stream_count; i++) { - struct media_stream *stream = source->streams[i]; - IMFStreamDescriptor *descriptor = stream->descriptor; + IMFStreamDescriptor *descriptor = source->descriptors[i]; - if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_source, source->stream_map[i], &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) WARN("Failed to set stream descriptor language, hr %#lx\n", hr); - if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, + if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_source, source->stream_map[i], &MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME))) WARN("Failed to set stream descriptor name, hr %#lx\n", hr); } + + /* reset the stream map to map wg_stream numbers instead */ + memset(source->stream_map, 0, source->stream_count * sizeof(*source->stream_map)); + for (i = 0; i < source->stream_count; i++) + { + if (FAILED(hr = map_stream_to_wg_parser_stream(source, i))) + WARN("Failed to map stream descriptor %u, hr %#lx\n", i, hr); + } } HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) @@ -1575,15 +1638,14 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d stream_count = wg_parser_get_stream_count(parser); for (i = 0; i < stream_count; ++i) { - struct wg_parser_stream *wg_stream = wg_parser_get_stream(parser, i); IMFStreamDescriptor *descriptor; struct media_stream *stream; struct wg_format format; - wg_parser_stream_get_preferred_format(wg_stream, &format); + wg_source_get_stream_format(wg_source, object->stream_map[i], &format); if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) goto fail; - if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, wg_stream, descriptor, &stream))) + if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, &stream))) { IMFStreamDescriptor_Release(descriptor); goto fail; diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 293c9c407de..d74e1fdf11c 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -46,6 +46,8 @@ extern bool link_element_to_sink(GstElement *element, GstPad *sink_pad) DECLSPEC extern GstCaps *detect_caps_from_data(const char *url, const void *data, guint size) DECLSPEC_HIDDEN; extern GstPad *create_pad_with_caps(GstPadDirection direction, GstCaps *caps) DECLSPEC_HIDDEN; extern GstBuffer *create_buffer_from_bytes(const void *data, guint size) DECLSPEC_HIDDEN; +extern gchar *stream_lang_from_tags(GstTagList *tags, GstCaps *caps) DECLSPEC_HIDDEN; +extern gchar *stream_name_from_tags(GstTagList *tags) DECLSPEC_HIDDEN; /* wg_format.c */ @@ -53,6 +55,9 @@ extern void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) D extern bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) DECLSPEC_HIDDEN; extern GstCaps *wg_format_to_caps(const struct wg_format *format) DECLSPEC_HIDDEN; +extern gchar *wg_stream_lang_from_tags(GstTagList *tags, GstCaps *caps) DECLSPEC_HIDDEN; +extern gchar *wg_stream_name_from_tags(GstTagList *tags) DECLSPEC_HIDDEN; + /* wg_transform.c */ extern NTSTATUS wg_transform_create(void *args) DECLSPEC_HIDDEN; @@ -70,6 +75,7 @@ extern NTSTATUS wg_source_destroy(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_push_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_get_stream_format(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_source_get_stream_tag(void *args) DECLSPEC_HIDDEN; /* wg_allocator.c */ diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 618897f0632..77f8dda0928 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -253,6 +253,56 @@ GstBuffer *create_buffer_from_bytes(const void *data, guint size) return buffer; } +gchar *stream_lang_from_tags(GstTagList *tags, GstCaps *caps) +{ + gchar *value; + + if (!gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &value) || !value) + return NULL; + + return value; +} + +gchar *stream_name_from_tags(GstTagList *tags) +{ + /* Extract stream name from Quick Time demuxer private tag where it puts unrecognized chunks. */ + guint i, tag_count = gst_tag_list_get_tag_size(tags, "private-qt-tag"); + gchar *value = NULL; + + for (i = 0; !value && i < tag_count; ++i) + { + const gchar *name; + const GValue *val; + GstSample *sample; + GstBuffer *buf; + gsize size; + + if (!(val = gst_tag_list_get_value_index(tags, "private-qt-tag", i))) + continue; + if (!GST_VALUE_HOLDS_SAMPLE(val) || !(sample = gst_value_get_sample(val))) + continue; + name = gst_structure_get_name(gst_sample_get_info(sample)); + if (!name || strcmp(name, "application/x-gst-qt-name-tag")) + continue; + if (!(buf = gst_sample_get_buffer(sample))) + continue; + if ((size = gst_buffer_get_size(buf)) < 8) + continue; + size -= 8; + if (!(value = g_malloc(size + 1))) + return NULL; + if (gst_buffer_extract(buf, 8, value, size) != size) + { + g_free(value); + value = NULL; + continue; + } + value[size] = 0; + } + + return value; +} + NTSTATUS wg_init_gstreamer(void *arg) { static GstGLContext *gl_context; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 7979beaeedb..2fe2a24e03b 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -368,6 +368,15 @@ struct wg_source_get_stream_format_params struct wg_format format; }; +struct wg_source_get_stream_tag_params +{ + struct wg_source *source; + UINT32 index; + enum wg_parser_tag tag; + UINT32 size; + char *buffer; +}; + enum unix_funcs { unix_wg_init_gstreamer, @@ -411,6 +420,7 @@ enum unix_funcs unix_wg_source_get_status, unix_wg_source_push_data, unix_wg_source_get_stream_format, + unix_wg_source_get_stream_tag, }; #endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index f5c26b2bf5c..4e2d9a38eb0 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1898,4 +1898,5 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_source_get_status), X(wg_source_push_data), X(wg_source_get_stream_format), + X(wg_source_get_stream_tag), }; diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 8b51d8c54cd..274420ffb09 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -69,6 +69,17 @@ static GstCaps *source_get_stream_caps(struct wg_source *source, guint index) return caps; } +static GstTagList *source_get_stream_tags(struct wg_source *source, guint index) +{ + GstStream *stream; + GstTagList *tags; + if (!(stream = source_get_stream(source, index))) + return NULL; + tags = gst_stream_get_tags(stream); + gst_object_unref(stream); + return tags; +} + static gboolean src_event_seek(struct wg_source *source, GstEvent *event) { guint32 seqnum = gst_event_get_seqnum(event); @@ -526,3 +537,62 @@ NTSTATUS wg_source_get_stream_format(void *args) return STATUS_SUCCESS; } +NTSTATUS wg_source_get_stream_tag(void *args) +{ + struct wg_source_get_stream_tag_params *params = args; + struct wg_source *source = params->source; + enum wg_parser_tag tag = params->tag; + guint index = params->index; + GstTagList *tags; + NTSTATUS status; + uint32_t len; + gchar *value; + + GST_TRACE("source %p, index %u, tag %u", source, index, tag); + + if (params->tag >= WG_PARSER_TAG_COUNT) + return STATUS_INVALID_PARAMETER; + if (!(tags = source_get_stream_tags(source, index))) + return STATUS_UNSUCCESSFUL; + + switch (tag) + { + case WG_PARSER_TAG_LANGUAGE: + { + GstCaps *caps = gst_pad_get_current_caps(source->src_pad); + value = stream_lang_from_tags(tags, caps); + if (caps) + gst_caps_unref(caps); + break; + } + case WG_PARSER_TAG_NAME: + value = stream_name_from_tags(tags); + break; + default: + GST_FIXME("Unsupported stream tag %u", tag); + value = NULL; + break; + } + + if (!value) + goto error; + + if ((len = strlen(value) + 1) > params->size) + { + params->size = len; + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + memcpy(params->buffer, value, len); + status = STATUS_SUCCESS; + } + + gst_tag_list_unref(tags); + g_free(value); + return status; + +error: + gst_tag_list_unref(tags); + return STATUS_NOT_FOUND; +} From 052d849c5a4a9bd4b311fa7a6c0aa8d27884fee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 28 Apr 2023 19:13:44 +0200 Subject: [PATCH 386/758] winegstreamer: Set MF_SD_MUTUALLY_EXCLUSIVE stream descriptor attribute. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e2a53247588..5d07ef30ff7 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1511,12 +1511,36 @@ static void media_source_init_stream_map(struct media_source *source, UINT strea static void media_source_init_descriptors(struct media_source *source) { + UINT i, last_audio = -1, last_video = -1; HRESULT hr; - UINT i; for (i = 0; i < source->stream_count; i++) { IMFStreamDescriptor *descriptor = source->descriptors[i]; + struct wg_format format = {0}; + UINT exclude = -1; + + if (FAILED(hr = wg_format_from_stream_descriptor(descriptor, &format))) + WARN("Failed to get format from stream descriptor, hr %#lx\n", hr); + + if (format.major_type == WG_MAJOR_TYPE_AUDIO) + { + exclude = last_audio; + last_audio = i; + } + else if (format.major_type == WG_MAJOR_TYPE_VIDEO) + { + exclude = last_video; + last_video = i; + } + + if (exclude != -1) + { + if (FAILED(IMFStreamDescriptor_SetUINT32(source->descriptors[exclude], &MF_SD_MUTUALLY_EXCLUSIVE, 1))) + WARN("Failed to set stream %u MF_SD_MUTUALLY_EXCLUSIVE\n", exclude); + else if (FAILED(IMFStreamDescriptor_SetUINT32(descriptor, &MF_SD_MUTUALLY_EXCLUSIVE, 1))) + WARN("Failed to set stream %u MF_SD_MUTUALLY_EXCLUSIVE\n", i); + } if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_source, source->stream_map[i], &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) From a287c3d208def9a179a904732b67b20c92edb62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 14:51:54 +0200 Subject: [PATCH 387/758] winegstreamer: Select one stream descriptor for each major type. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5d07ef30ff7..92cddc3721b 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -992,9 +992,6 @@ static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor * IMFStreamDescriptor_AddRef(descriptor); object->descriptor = descriptor; - object->active = TRUE; - object->eos = FALSE; - TRACE("Created stream object %p.\n", object); *out = object; @@ -1511,7 +1508,7 @@ static void media_source_init_stream_map(struct media_source *source, UINT strea static void media_source_init_descriptors(struct media_source *source) { - UINT i, last_audio = -1, last_video = -1; + UINT i, last_audio = -1, last_video = -1, first_audio = -1, first_video = -1; HRESULT hr; for (i = 0; i < source->stream_count; i++) @@ -1525,11 +1522,15 @@ static void media_source_init_descriptors(struct media_source *source) if (format.major_type == WG_MAJOR_TYPE_AUDIO) { + if (first_audio == -1) + first_audio = i; exclude = last_audio; last_audio = i; } else if (format.major_type == WG_MAJOR_TYPE_VIDEO) { + if (first_video == -1) + first_video = i; exclude = last_video; last_video = i; } @@ -1557,6 +1558,21 @@ static void media_source_init_descriptors(struct media_source *source) if (FAILED(hr = map_stream_to_wg_parser_stream(source, i))) WARN("Failed to map stream descriptor %u, hr %#lx\n", i, hr); } + + if (!wcscmp(source->mime_type, L"video/mp4")) + { + if (last_audio != -1) + source->streams[last_audio]->active = TRUE; + if (last_video != -1) + source->streams[last_video]->active = TRUE; + } + else + { + if (first_audio != -1) + source->streams[first_audio]->active = TRUE; + if (first_video != -1) + source->streams[first_video]->active = TRUE; + } } HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *data, UINT64 size, IMFMediaSource **out) From 842fd143782f5ceee84c1be3ab009030583d7ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 29 Apr 2023 19:15:31 +0200 Subject: [PATCH 388/758] winegstreamer: Use 1-based ids in media source stream descriptors. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 92cddc3721b..9e3dc2a35de 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -366,7 +366,7 @@ static HRESULT media_stream_get_wg_parser_stream(struct media_stream *stream, st if (FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &id))) return hr; - if (!(id = source->stream_map[id]) || !(*wg_stream = wg_parser_get_stream(source->wg_parser, id - 1))) + if (!(id = source->stream_map[id - 1]) || !(*wg_stream = wg_parser_get_stream(source->wg_parser, id - 1))) return MF_E_INVALIDSTREAMNUMBER; return S_OK; } @@ -483,7 +483,7 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe else if (!selected || FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream_descriptor, &id))) IMFStreamDescriptor_Release(stream_descriptor); else - descriptors[id] = stream_descriptor; + descriptors[id - 1] = stream_descriptor; } source->state = SOURCE_RUNNING; @@ -1683,7 +1683,7 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d struct wg_format format; wg_source_get_stream_format(wg_source, object->stream_map[i], &format); - if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) + if (FAILED(hr = stream_descriptor_create(i + 1, &format, &descriptor))) goto fail; if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, &stream))) { From a62f1f1f058403ebb9b1a03899a62d45fcc78776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 10:16:05 +0200 Subject: [PATCH 389/758] winegstreamer: Delay wg_parser creation to media source start. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 66 ++++++++++++++----------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 9e3dc2a35de..56b46a4e3cf 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -445,6 +445,8 @@ static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL &GUID_NULL, S_OK, position); } +static DWORD CALLBACK read_thread(void *arg); + static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, GUID *format, PROPVARIANT *position) { @@ -459,6 +461,30 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe if (source->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN; + if (!source->wg_parser) + { + /* In Media Foundation, sources may read from any media source stream + * without fear of blocking due to buffering limits on another. Trailmakers, + * a Unity3D Engine game, only reads one sample from the audio stream (and + * never deselects it). Remove buffering limits from decodebin in order to + * account for this. Note that this does leak memory, but the same memory + * leak occurs with native. */ + if (!(source->wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, true))) + return E_OUTOFMEMORY; + if (!(source->read_thread = CreateThread(NULL, 0, read_thread, source, 0, NULL))) + return E_OUTOFMEMORY; + if (FAILED(hr = wg_parser_connect(source->wg_parser, source->file_size, NULL))) + return hr; + + /* reset the stream map to map wg_stream numbers instead */ + memset(source->stream_map, 0, source->stream_count * sizeof(*source->stream_map)); + for (i = 0; i < source->stream_count; i++) + { + if (FAILED(hr = map_stream_to_wg_parser_stream(source, i))) + WARN("Failed to map stream %lu, hr %#lx\n", i, hr); + } + } + /* seek to beginning on stop->play */ if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) { @@ -1225,7 +1251,8 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); wg_source_destroy(source->wg_source); - wg_parser_destroy(source->wg_parser); + if (source->wg_parser) + wg_parser_destroy(source->wg_parser); source->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&source->cs); free(source); @@ -1422,7 +1449,8 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) source->state = SOURCE_SHUTDOWN; - wg_parser_disconnect(source->wg_parser); + if (source->wg_parser) + wg_parser_disconnect(source->wg_parser); source->read_thread_shutdown = true; WaitForSingleObject(source->read_thread, INFINITE); @@ -1551,14 +1579,6 @@ static void media_source_init_descriptors(struct media_source *source) WARN("Failed to set stream descriptor name, hr %#lx\n", hr); } - /* reset the stream map to map wg_stream numbers instead */ - memset(source->stream_map, 0, source->stream_count * sizeof(*source->stream_map)); - for (i = 0; i < source->stream_count; i++) - { - if (FAILED(hr = map_stream_to_wg_parser_stream(source, i))) - WARN("Failed to map stream descriptor %u, hr %#lx\n", i, hr); - } - if (!wcscmp(source->mime_type, L"video/mp4")) { if (last_audio != -1) @@ -1581,7 +1601,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d UINT32 stream_count; struct media_source *object; struct wg_source *wg_source; - struct wg_parser *parser; DWORD bytestream_caps, read_size = size; WCHAR mime_type[256]; unsigned int i; @@ -1649,20 +1668,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) goto fail; - if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, false))) - { - hr = E_OUTOFMEMORY; - goto fail; - } - object->wg_parser = parser; - - object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL); - - object->state = SOURCE_OPENING; - - if (FAILED(hr = wg_parser_connect(parser, file_size, NULL))) - goto fail; - if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) || !(object->stream_map = calloc(stream_count, sizeof(*object->stream_map))) || !(object->streams = calloc(stream_count, sizeof(*object->streams)))) @@ -1675,7 +1680,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d media_source_init_stream_map(object, stream_count); - stream_count = wg_parser_get_stream_count(parser); for (i = 0; i < stream_count; ++i) { IMFStreamDescriptor *descriptor; @@ -1716,16 +1720,6 @@ HRESULT media_source_create(IMFByteStream *bytestream, const WCHAR *url, BYTE *d free(object->descriptors); free(object->streams); - if (object->state == SOURCE_OPENING) - wg_parser_disconnect(object->wg_parser); - if (object->read_thread) - { - object->read_thread_shutdown = true; - WaitForSingleObject(object->read_thread, INFINITE); - CloseHandle(object->read_thread); - } - if (object->wg_parser) - wg_parser_destroy(object->wg_parser); if (object->async_commands_queue) MFUnlockWorkQueue(object->async_commands_queue); if (object->event_queue) From d6122e4f41c9c4dd4bc567a81980a059814d8d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 13 Mar 2023 17:00:39 +0100 Subject: [PATCH 390/758] HACK: winegstreamer: Fake raw formats for wg_source streams. CW-Bug-Id: #21953 --- dlls/winegstreamer/wg_source.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/wg_source.c b/dlls/winegstreamer/wg_source.c index 274420ffb09..114a0e60582 100644 --- a/dlls/winegstreamer/wg_source.c +++ b/dlls/winegstreamer/wg_source.c @@ -525,14 +525,32 @@ NTSTATUS wg_source_get_stream_format(void *args) struct wg_source_get_stream_format_params *params = args; struct wg_source *source = params->source; guint index = params->index; - GstCaps *caps; + GstCaps *caps, *copy; GST_TRACE("source %p, index %u", source, index); if (!(caps = source_get_stream_caps(source, index))) return STATUS_UNSUCCESSFUL; - wg_format_from_caps(¶ms->format, caps); + if (!(copy = gst_caps_copy(caps))) + goto done; + switch (stream_type_from_caps(caps)) + { + case GST_STREAM_TYPE_VIDEO: + gst_structure_set_name(gst_caps_get_structure(copy, 0), "video/x-raw"); + gst_caps_set_simple(copy, "format", G_TYPE_STRING, "NV12", NULL); + break; + case GST_STREAM_TYPE_AUDIO: + gst_structure_set_name(gst_caps_get_structure(copy, 0), "audio/x-raw"); + gst_caps_set_simple(copy, "format", G_TYPE_STRING, "S16LE", NULL); + gst_caps_set_simple(copy, "layout", G_TYPE_STRING, "interleaved", NULL); + break; + default: break; + } + wg_format_from_caps(¶ms->format, copy); + gst_caps_unref(copy); + +done: gst_caps_unref(caps); return STATUS_SUCCESS; } From 55dede50b929a4c553304b60d688370adff7e1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 3 May 2023 11:10:01 +0200 Subject: [PATCH 391/758] HACK: winegstreamer: Enable new media source for Bloodstained. CW-Bug-Id: #21953 --- dlls/winegstreamer/mf_handler.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index 7472f8c324a..bd1bd810c96 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -173,7 +173,9 @@ static HRESULT async_create_object_complete(struct async_create_object *async, if (async->flags & MF_RESOLUTION_MEDIASOURCE) { - const char *env = getenv("WINE_NEW_MEDIA_SOURCE"); + const char *sgi, *env = getenv("WINE_NEW_MEDIA_SOURCE"); + if (!env && (sgi = getenv("SteamGameId")) && (!strcmp(sgi, "692850"))) env = "1"; + if (!async->stream) hr = media_source_create_from_url(async->url, (IMFMediaSource **)&object); else if (!env || !atoi(env)) From cf768ea4772e327df853a5d48afc32ee43f721a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 12 May 2023 13:34:23 +0200 Subject: [PATCH 392/758] winegstreamer: Introduce and use a new wg_task_pool helper. To better track GStreamer threads, avoiding potential task leaks in the default pool which keeps some thread alive. CW-Bug-Id: #22045 --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/unix_private.h | 4 ++ dlls/winegstreamer/wg_parser.c | 28 +++++++++ dlls/winegstreamer/wg_task_pool.c | 98 +++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 dlls/winegstreamer/wg_task_pool.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index acf4840e1d6..d811cfd810c 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -26,6 +26,7 @@ C_SRCS = \ wg_parser.c \ wg_sample.c \ wg_source.c \ + wg_task_pool.c \ wg_transform.c \ wm_reader.c \ wma_decoder.c \ diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index d74e1fdf11c..1f4ebf0a2df 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -77,6 +77,10 @@ extern NTSTATUS wg_source_push_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_get_stream_format(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_source_get_stream_tag(void *args) DECLSPEC_HIDDEN; +/* wg_task_pool.c */ + +extern GstTaskPool *wg_task_pool_new(void) DECLSPEC_HIDDEN; + /* wg_allocator.c */ /* wg_allocator_release_sample can be used to release any sample that was requested. */ diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 4e2d9a38eb0..01885f72147 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -64,6 +64,7 @@ struct wg_parser GstElement *container, *decodebin; GstBus *bus; + GstTaskPool *task_pool; GstPad *my_src; guint64 file_size, start_offset, next_offset, stop_offset; @@ -1304,6 +1305,24 @@ static GstBusSyncReply bus_handler_cb(GstBus *bus, GstMessage *msg, gpointer use } break; + case GST_MESSAGE_STREAM_STATUS: + { + GstStreamStatusType type; + GstElement *element; + const GValue *val; + GstTask *task; + + gst_message_parse_stream_status(msg, &type, &element); + val = gst_message_get_stream_status_object(msg); + GST_DEBUG("parser %p, message %s, type %u, value %p (%s).", parser, GST_MESSAGE_TYPE_NAME(msg), type, val, G_VALUE_TYPE_NAME(val)); + + if (G_VALUE_TYPE(val) == GST_TYPE_TASK && (task = g_value_get_object(val)) + && type == GST_STREAM_STATUS_TYPE_CREATE) + gst_task_set_pool(task, parser->task_pool); + + break; + } + default: break; } @@ -1664,6 +1683,7 @@ static NTSTATUS wg_parser_disconnect(void *args) gst_object_unref(parser->container); parser->container = NULL; + gst_task_pool_cleanup(parser->task_pool); return S_OK; } @@ -1805,6 +1825,7 @@ static NTSTATUS wg_parser_create(void *args) struct wg_parser_create_params *params = args; struct wg_parser *parser; + GError *error; if (!(parser = calloc(1, sizeof(*parser)))) return E_OUTOFMEMORY; @@ -1818,6 +1839,12 @@ static NTSTATUS wg_parser_create(void *args) parser->use_opengl = FALSE; } } + if (!(parser->task_pool = wg_task_pool_new())) + { + free(parser); + return E_OUTOFMEMORY; + } + gst_task_pool_prepare(parser->task_pool, &error); pthread_mutex_init(&parser->mutex, NULL); pthread_cond_init(&parser->init_cond, NULL); @@ -1840,6 +1867,7 @@ static NTSTATUS wg_parser_destroy(void *args) gst_bus_set_sync_handler(parser->bus, NULL, NULL, NULL); gst_object_unref(parser->bus); } + gst_object_unref(parser->task_pool); if (parser->context) gst_context_unref(parser->context); diff --git a/dlls/winegstreamer/wg_task_pool.c b/dlls/winegstreamer/wg_task_pool.c new file mode 100644 index 00000000000..ec7da286d5f --- /dev/null +++ b/dlls/winegstreamer/wg_task_pool.c @@ -0,0 +1,98 @@ +/* + * GStreamer task pool + * + * Copyright 2023 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include +#include + +#include + +#include "unix_private.h" + +typedef struct +{ + GstTaskPool parent; +} WgTaskPool; + +typedef struct +{ + GstTaskPoolClass parent_class; +} WgTaskPoolClass; + +G_DEFINE_TYPE(WgTaskPool, wg_task_pool, GST_TYPE_TASK_POOL); + +static void wg_task_pool_prepare(GstTaskPool *pool, GError **error) +{ + GST_LOG("pool %p, error %p", pool, error); +} + +static void wg_task_pool_cleanup(GstTaskPool *pool) +{ + GST_LOG("pool %p", pool); +} + +static gpointer wg_task_pool_push(GstTaskPool *pool, GstTaskPoolFunction func, gpointer data, GError **error) +{ + pthread_t *tid; + gint res; + + GST_LOG("pool %p, func %p, data %p, error %p", pool, func, data, error); + + if (!(tid = malloc(sizeof(*tid))) || !(res = pthread_create(tid, NULL, (void *)func, data))) + return tid; + + g_set_error(error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, "Error creating thread: %s", g_strerror(res)); + free(tid); + + return NULL; +} + +static void wg_task_pool_join(GstTaskPool *pool, gpointer id) +{ + pthread_t *tid = id; + + GST_LOG("pool %p, id %p", pool, id); + + pthread_join(*tid, NULL); + free(tid); +} + +static void wg_task_pool_class_init(WgTaskPoolClass *klass) +{ + GstTaskPoolClass *parent_class = (GstTaskPoolClass *)klass; + parent_class->prepare = wg_task_pool_prepare; + parent_class->cleanup = wg_task_pool_cleanup; + parent_class->push = wg_task_pool_push; + parent_class->join = wg_task_pool_join; +} + +static void wg_task_pool_init(WgTaskPool *pool) +{ +} + +GstTaskPool *wg_task_pool_new(void) +{ + return g_object_new(wg_task_pool_get_type(), NULL); +} From 6abf9d9cb98cdfd00b079f189399390dce33d653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 16 May 2023 11:44:16 +0200 Subject: [PATCH 393/758] HACK: winegstreamer: Don't use opengl for the new media source wg_parser. As a hack because the parameter has a different meaning upstream. CW-Bug-Id: #21953 --- dlls/winegstreamer/media_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 56b46a4e3cf..3cb57e94384 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -469,7 +469,7 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe * never deselects it). Remove buffering limits from decodebin in order to * account for this. Note that this does leak memory, but the same memory * leak occurs with native. */ - if (!(source->wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, true))) + if (!(source->wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, false))) return E_OUTOFMEMORY; if (!(source->read_thread = CreateThread(NULL, 0, read_thread, source, 0, NULL))) return E_OUTOFMEMORY; From 05d4663f406b4c7caf8056a305deedb728167ce9 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 394/758] winegstreamer: Check the absolute value of the height in mf_media_type_from_wg_format_video(). Inverted height indicates vertically flipped video. We don't currently handle that either, but one thing at a time. (cherry picked from commit 98c266c0bdd02c4569fb2acaf70da66e66b79c72) --- dlls/winegstreamer/mfplat.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 27ec2aaea86..24bb092d39c 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -518,13 +518,15 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * { if (format->u.video.format == video_formats[i].format) { + int32_t height = abs(format->u.video.height); + int32_t width = format->u.video.width; + if (FAILED(MFCreateMediaType(&type))) return NULL; IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, video_formats[i].subtype); - IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, - make_uint64(format->u.video.width, format->u.video.height)); + IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, make_uint64(width, height)); IMFMediaType_SetUINT64(type, &MF_MT_FRAME_RATE, make_uint64(format->u.video.fps_n, format->u.video.fps_d)); IMFMediaType_SetUINT32(type, &MF_MT_COMPRESSED, FALSE); @@ -537,8 +539,8 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * { .OffsetX = {.value = format->u.video.padding.left}, .OffsetY = {.value = format->u.video.padding.top}, - .Area.cx = format->u.video.width - format->u.video.padding.right - format->u.video.padding.left, - .Area.cy = format->u.video.height - format->u.video.padding.bottom - format->u.video.padding.top, + .Area.cx = width - format->u.video.padding.right - format->u.video.padding.left, + .Area.cy = height - format->u.video.padding.bottom - format->u.video.padding.top, }; IMFMediaType_SetBlob(type, &MF_MT_MINIMUM_DISPLAY_APERTURE, From 56e8f8e68b1d47cda114b06242f5b54fac86d809 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 395/758] winegstreamer: Separate a mf_video_format_to_wg() helper. (cherry picked from commit 2803a220f684869ccd66e78778133b2586282da9) --- dlls/winegstreamer/mfplat.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 24bb092d39c..c39fc20b510 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -682,11 +682,23 @@ static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUI format->u.audio_mpeg4.codec_data_len = codec_data_size; } +static enum wg_video_format mf_video_format_to_wg(const GUID *subtype) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(video_formats); ++i) + { + if (IsEqualGUID(subtype, video_formats[i].subtype)) + return video_formats[i].format; + } + FIXME("Unrecognized video subtype %s.\n", debugstr_guid(subtype)); + return WG_VIDEO_FORMAT_UNKNOWN; +} + static void mf_media_type_to_wg_format_video(IMFMediaType *type, const GUID *subtype, struct wg_format *format) { UINT64 frame_rate, frame_size; MFVideoArea aperture; - unsigned int i; UINT32 size; if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) @@ -716,15 +728,7 @@ static void mf_media_type_to_wg_format_video(IMFMediaType *type, const GUID *sub format->u.video.fps_d = (UINT32)frame_rate; } - for (i = 0; i < ARRAY_SIZE(video_formats); ++i) - { - if (IsEqualGUID(subtype, video_formats[i].subtype)) - { - format->u.video.format = video_formats[i].format; - return; - } - } - FIXME("Unrecognized video subtype %s.\n", debugstr_guid(subtype)); + format->u.video.format = mf_video_format_to_wg(subtype); } static void mf_media_type_to_wg_format_audio_wma(IMFMediaType *type, const GUID *subtype, struct wg_format *format) From b1276584df0b0eef544b5963513890516bcb96c2 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 396/758] winegstreamer: Initialize media source video types from a wg_video_format array. The mf_media_type_from_wg_format function will use the video format to calculate stride. (adapted from commit 98d209752cee9abd8dc31dfe1f28811066b0b83f) --- dlls/winegstreamer/media_source_old.c | 49 ++++++++++++++++----------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/dlls/winegstreamer/media_source_old.c b/dlls/winegstreamer/media_source_old.c index a2fe13e4da8..624ecc86cad 100644 --- a/dlls/winegstreamer/media_source_old.c +++ b/dlls/winegstreamer/media_source_old.c @@ -887,18 +887,18 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) if (format.major_type == WG_MAJOR_TYPE_VIDEO) { - /* These are the most common native output types of decoders: - https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */ - static const GUID *const video_types[] = + /* Try to prefer YUV formats over RGB ones. Most decoders output in the + * YUV color space, and it's generally much less expensive for + * videoconvert to do YUV -> YUV transformations. */ + static const enum wg_video_format video_formats[] = { - &MFVideoFormat_NV12, - &MFVideoFormat_YV12, - &MFVideoFormat_YUY2, - &MFVideoFormat_IYUV, - &MFVideoFormat_I420, - &MFVideoFormat_ARGB32, - &MFVideoFormat_RGB32, - &MFVideoFormat_ABGR32, + WG_VIDEO_FORMAT_NV12, + WG_VIDEO_FORMAT_YV12, + WG_VIDEO_FORMAT_YUY2, + WG_VIDEO_FORMAT_I420, + WG_VIDEO_FORMAT_BGRA, + WG_VIDEO_FORMAT_BGRx, + WG_VIDEO_FORMAT_RGBA, }; IMFMediaType *base_type = mf_media_type_from_wg_format(&format); @@ -917,21 +917,32 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) stream_types[0] = base_type; type_count = 1; - for (i = 0; i < ARRAY_SIZE(video_types); i++) + for (i = 0; i < ARRAY_SIZE(video_formats); ++i) { + struct wg_format new_format = format; IMFMediaType *new_type; - if (IsEqualGUID(&base_subtype, video_types[i])) - continue; + new_format.u.video.format = video_formats[i]; - if (FAILED(hr = MFCreateMediaType(&new_type))) + if (!(new_type = mf_media_type_from_wg_format(&new_format))) + { + hr = E_OUTOFMEMORY; goto done; + } stream_types[type_count++] = new_type; - if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type))) - goto done; - if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i]))) - goto done; + if (video_formats[i] == WG_VIDEO_FORMAT_I420) + { + IMFMediaType *iyuv_type; + + if (FAILED(hr = MFCreateMediaType(&iyuv_type))) + goto done; + if (FAILED(hr = IMFMediaType_CopyAllItems(new_type, (IMFAttributes *)iyuv_type))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(iyuv_type, &MF_MT_SUBTYPE, &MFVideoFormat_IYUV))) + goto done; + stream_types[type_count++] = iyuv_type; + } } } else if (format.major_type == WG_MAJOR_TYPE_AUDIO) From 2e029fa654ee3a2b74fc0556f8a399f31d914b0a Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 397/758] winegstreamer: Set the MF_MT_DEFAULT_STRIDE attribute in mf_media_type_from_wg_format(). (partial import of ef354da59097b206122ec9101ee1f44201cca466) --- dlls/winegstreamer/gst_private.h | 4 ++ dlls/winegstreamer/main.c | 66 ++++++++++++++++++++++++++++++++ dlls/winegstreamer/mfplat.c | 7 ++++ 3 files changed, 77 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index b31feddd5ef..7103793cf50 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -165,6 +165,10 @@ HRESULT media_source_create(IMFByteStream *stream, const WCHAR *url, BYTE *data, HRESULT media_source_create_old(IMFByteStream *stream, const WCHAR *url, IMFMediaSource **out); HRESULT media_source_create_from_url(const WCHAR *url, IMFMediaSource **out); +unsigned int wg_format_get_stride(const struct wg_format *format); + +bool wg_video_format_is_rgb(enum wg_video_format format); + HRESULT aac_decoder_create(REFIID riid, void **ret); HRESULT h264_decoder_create(REFIID riid, void **ret); HRESULT video_processor_create(REFIID riid, void **ret); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 6175fc94a01..b0afca51d74 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -544,6 +544,72 @@ char *wg_source_get_stream_tag(struct wg_source *source, UINT32 index, enum wg_p return buffer; } +#define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1)) + +unsigned int wg_format_get_stride(const struct wg_format *format) +{ + const unsigned int width = format->u.video.width; + + switch (format->u.video.format) + { + case WG_VIDEO_FORMAT_AYUV: + return width * 4; + + case WG_VIDEO_FORMAT_BGRA: + case WG_VIDEO_FORMAT_BGRx: + case WG_VIDEO_FORMAT_RGBA: + return width * 4; + + case WG_VIDEO_FORMAT_BGR: + return ALIGN(width * 3, 4); + + case WG_VIDEO_FORMAT_UYVY: + case WG_VIDEO_FORMAT_YUY2: + case WG_VIDEO_FORMAT_YVYU: + return ALIGN(width * 2, 4); + + case WG_VIDEO_FORMAT_RGB15: + case WG_VIDEO_FORMAT_RGB16: + return ALIGN(width * 2, 4); + + case WG_VIDEO_FORMAT_I420: + case WG_VIDEO_FORMAT_NV12: + case WG_VIDEO_FORMAT_YV12: + return ALIGN(width, 4); /* Y plane */ + + case WG_VIDEO_FORMAT_UNKNOWN: + FIXME("Cannot calculate stride for unknown video format.\n"); + } + + return 0; +} + +bool wg_video_format_is_rgb(enum wg_video_format format) +{ + switch (format) + { + case WG_VIDEO_FORMAT_BGRA: + case WG_VIDEO_FORMAT_BGRx: + case WG_VIDEO_FORMAT_RGBA: + case WG_VIDEO_FORMAT_BGR: + case WG_VIDEO_FORMAT_RGB15: + case WG_VIDEO_FORMAT_RGB16: + return true; + + case WG_VIDEO_FORMAT_AYUV: + case WG_VIDEO_FORMAT_I420: + case WG_VIDEO_FORMAT_NV12: + case WG_VIDEO_FORMAT_UYVY: + case WG_VIDEO_FORMAT_YUY2: + case WG_VIDEO_FORMAT_YV12: + case WG_VIDEO_FORMAT_YVYU: + case WG_VIDEO_FORMAT_UNKNOWN: + break; + } + + return false; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_ATTACH) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index c39fc20b510..78e823e003f 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -518,6 +518,7 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * { if (format->u.video.format == video_formats[i].format) { + unsigned int stride = wg_format_get_stride(format); int32_t height = abs(format->u.video.height); int32_t width = format->u.video.width; @@ -533,6 +534,12 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); IMFMediaType_SetUINT32(type, &MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0); + if (wg_video_format_is_rgb(format->u.video.format)) + stride = -stride; + if (format->u.video.height < 0) + stride = -stride; + IMFMediaType_SetUINT32(type, &MF_MT_DEFAULT_STRIDE, stride); + if (!IsRectEmpty(&format->u.video.padding)) { MFVideoArea aperture = From 0f91b69ea9a4cba4cfbab501024d653c2a9239e0 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 398/758] winegstreamer: Translate the MF_MT_DEFAULT_STRIDE attribute to flipped video in mf_media_type_to_wg_format(). (cherry picked from commit af82c44e03ab5355f60b0796b5420b3688ea86a3) --- dlls/winegstreamer/mfplat.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 78e823e003f..48c2bb39657 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -706,7 +706,7 @@ static void mf_media_type_to_wg_format_video(IMFMediaType *type, const GUID *sub { UINT64 frame_rate, frame_size; MFVideoArea aperture; - UINT32 size; + UINT32 size, stride; if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) { @@ -736,6 +736,14 @@ static void mf_media_type_to_wg_format_video(IMFMediaType *type, const GUID *sub } format->u.video.format = mf_video_format_to_wg(subtype); + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_DEFAULT_STRIDE, &stride))) + { + if (wg_video_format_is_rgb(format->u.video.format)) + format->u.video.height = -format->u.video.height; + if ((int)stride < 0) + format->u.video.height = -format->u.video.height; + } } static void mf_media_type_to_wg_format_audio_wma(IMFMediaType *type, const GUID *subtype, struct wg_format *format) From 1e3fbdf02a8fa935818c1ff26de7981cb0072eb8 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 399/758] winegstreamer: Move flipping based on RGB to the frontends. Give the backend a more simple and self-consistent API. This commit also changes behaviour, by virtue of *not* changing some frontends. In specific, this commit does not modify the AVI splitter, which is known to output top-down RGB samples on Windows if the original video uses them (this is trivial to test by modifying test.avi in quartz to use "bgra" instead of "yuv420p"). It also does not modify the Media Foundation color converter DMO, whose tests imply that the MF_MT_DEFAULT_STRIDE attribute is always positive. (adapted from commit 6c11a4af5885c6485a908ea5c547e072fe71aa23) --- dlls/winegstreamer/mfplat.c | 8 ++++---- dlls/winegstreamer/quartz_parser.c | 9 ++++++++- dlls/winegstreamer/unixlib.h | 2 ++ dlls/winegstreamer/wg_parser.c | 29 ++--------------------------- dlls/winegstreamer/wm_reader.c | 9 ++++++++- dlls/winegstreamer/wmv_decoder.c | 15 ++++++++++++--- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 48c2bb39657..9e0f3db23bb 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -534,8 +534,6 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); IMFMediaType_SetUINT32(type, &MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0); - if (wg_video_format_is_rgb(format->u.video.format)) - stride = -stride; if (format->u.video.height < 0) stride = -stride; IMFMediaType_SetUINT32(type, &MF_MT_DEFAULT_STRIDE, stride); @@ -739,11 +737,13 @@ static void mf_media_type_to_wg_format_video(IMFMediaType *type, const GUID *sub if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_DEFAULT_STRIDE, &stride))) { - if (wg_video_format_is_rgb(format->u.video.format)) - format->u.video.height = -format->u.video.height; if ((int)stride < 0) format->u.video.height = -format->u.video.height; } + else if (wg_video_format_is_rgb(format->u.video.format)) + { + format->u.video.height = -format->u.video.height; + } } static void mf_media_type_to_wg_format_audio_wma(IMFMediaType *type, const GUID *subtype, struct wg_format *format) diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index 4f91b2584e2..dfbe62a7a81 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -546,7 +546,7 @@ static bool amt_from_wg_format_video(AM_MEDIA_TYPE *mt, const struct wg_format * if (wm) { - SetRect(&video_format->rcSource, 0, 0, format->u.video.width, format->u.video.height); + SetRect(&video_format->rcSource, 0, 0, format->u.video.width, abs(format->u.video.height)); video_format->rcTarget = video_format->rcSource; } if ((frame_time = MulDiv(10000000, format->u.video.fps_d, format->u.video.fps_n)) != -1) @@ -554,6 +554,8 @@ static bool amt_from_wg_format_video(AM_MEDIA_TYPE *mt, const struct wg_format * video_format->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); video_format->bmiHeader.biWidth = format->u.video.width; video_format->bmiHeader.biHeight = format->u.video.height; + if (wg_video_format_is_rgb(format->u.video.format)) + video_format->bmiHeader.biHeight = -format->u.video.height; video_format->bmiHeader.biPlanes = 1; video_format->bmiHeader.biBitCount = wg_video_format_get_depth(format->u.video.format); video_format->bmiHeader.biCompression = wg_video_format_get_compression(format->u.video.format); @@ -826,6 +828,8 @@ static bool amt_to_wg_format_video(const AM_MEDIA_TYPE *mt, struct wg_format *fo if (IsEqualGUID(&mt->subtype, format_map[i].subtype)) { format->u.video.format = format_map[i].format; + if (wg_video_format_is_rgb(format->u.video.format)) + format->u.video.height = -format->u.video.height; return true; } } @@ -1470,6 +1474,9 @@ static HRESULT decodebin_parser_source_get_media_type(struct parser_source *pin, if (format.major_type == WG_MAJOR_TYPE_VIDEO && index < ARRAY_SIZE(video_formats)) { format.u.video.format = video_formats[index]; + /* Downstream filters probably expect RGB video to be bottom-up. */ + if (format.u.video.height > 0 && wg_video_format_is_rgb(video_formats[index])) + format.u.video.height = -format.u.video.height; if (!amt_from_wg_format(mt, &format, false)) return E_OUTOFMEMORY; return S_OK; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 2fe2a24e03b..79f151bd965 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -112,6 +112,8 @@ struct wg_format WG_VIDEO_FORMAT_YV12, WG_VIDEO_FORMAT_YVYU, } format; + /* Positive height indicates top-down video; negative height + * indicates bottom-up video. */ int32_t width, height; uint32_t fps_n, fps_d; RECT padding; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 01885f72147..83c939b8453 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -225,34 +225,9 @@ static NTSTATUS wg_parser_stream_enable(void *args) if (format->major_type == WG_MAJOR_TYPE_VIDEO) { - if (params->flags & STREAM_ENABLE_FLAG_FLIP_RGB) - { - bool flip = (format->u.video.height < 0); - - switch (format->u.video.format) - { - case WG_VIDEO_FORMAT_BGRA: - case WG_VIDEO_FORMAT_BGRx: - case WG_VIDEO_FORMAT_BGR: - case WG_VIDEO_FORMAT_RGBA: - case WG_VIDEO_FORMAT_RGB15: - case WG_VIDEO_FORMAT_RGB16: - flip = !flip; - break; + bool flip = (params->flags & STREAM_ENABLE_FLAG_FLIP_RGB) && (format->u.video.height < 0); - case WG_VIDEO_FORMAT_AYUV: - case WG_VIDEO_FORMAT_I420: - case WG_VIDEO_FORMAT_NV12: - case WG_VIDEO_FORMAT_UYVY: - case WG_VIDEO_FORMAT_YUY2: - case WG_VIDEO_FORMAT_YV12: - case WG_VIDEO_FORMAT_YVYU: - case WG_VIDEO_FORMAT_UNKNOWN: - break; - } - - gst_util_set_object_arg(G_OBJECT(stream->flip), "method", flip ? "vertical-flip" : "none"); - } + gst_util_set_object_arg(G_OBJECT(stream->flip), "method", flip ? "vertical-flip" : "none"); } gst_pad_push_event(stream->my_sink, gst_event_new_reconfigure()); diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 5e86b4f2b33..05b6960abe4 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1522,6 +1522,10 @@ static HRESULT init_stream(struct wm_reader *reader, QWORD file_size) if (id && !strcmp(id, "1113000")) stream->format.u.video.format = WG_VIDEO_FORMAT_BGRx; } + + /* API consumers expect RGB video to be bottom-up. */ + if (stream->format.u.video.height > 0) + stream->format.u.video.height = -stream->format.u.video.height; } wg_parser_stream_enable(stream->wg_stream, &stream->format, STREAM_ENABLE_FLAG_FLIP_RGB); } @@ -1935,6 +1939,9 @@ static HRESULT WINAPI reader_GetOutputFormat(IWMSyncReader2 *iface, return NS_E_INVALID_OUTPUT_FORMAT; } format.u.video.format = video_formats[index]; + /* API consumers expect RGB video to be bottom-up. */ + if (format.u.video.height > 0 && wg_video_format_is_rgb(format.u.video.format)) + format.u.video.height = -format.u.video.height; break; case WG_MAJOR_TYPE_AUDIO: @@ -2227,7 +2234,7 @@ static HRESULT WINAPI reader_SetOutputProps(IWMSyncReader2 *iface, DWORD output, hr = NS_E_INVALID_OUTPUT_FORMAT; else if (pref_format.u.video.width != format.u.video.width) hr = NS_E_INVALID_OUTPUT_FORMAT; - else if (pref_format.u.video.height != format.u.video.height) + else if (abs(pref_format.u.video.height) != abs(format.u.video.height)) hr = NS_E_INVALID_OUTPUT_FORMAT; break; diff --git a/dlls/winegstreamer/wmv_decoder.c b/dlls/winegstreamer/wmv_decoder.c index b81639f312a..9af4a18a5fd 100644 --- a/dlls/winegstreamer/wmv_decoder.c +++ b/dlls/winegstreamer/wmv_decoder.c @@ -382,7 +382,12 @@ static HRESULT WINAPI media_object_GetInputType(IMediaObject *iface, DWORD index if (!format.u.video.width) format.u.video.width = 1920; if (!format.u.video.height) - format.u.video.height = 1080; + { + if (wg_video_format_is_rgb(format.u.video.format)) + format.u.video.height = -1080; + else + format.u.video.height = 1080; + } if (!format.u.video.fps_d) format.u.video.fps_d = 1; if (!format.u.video.fps_n) @@ -410,12 +415,16 @@ static HRESULT WINAPI media_object_GetOutputType(IMediaObject *iface, DWORD inde if (!format.u.video.width) format.u.video.width = 1920; if (!format.u.video.height) - format.u.video.height = 1080; + { + if (wg_video_format_is_rgb(format.u.video.format)) + format.u.video.height = -1080; + else + format.u.video.height = 1080; + } if (!format.u.video.fps_d) format.u.video.fps_d = 1; if (!format.u.video.fps_n) format.u.video.fps_n = 1; - if (!amt_from_wg_format((AM_MEDIA_TYPE *)type, &format, false)) return VFW_E_NO_TYPES; From d6802147c97b0ce241d60106e38dccdaa20b1ff7 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 400/758] winegstreamer: Fix negative height image size calculation. Fixes intro video playback in multiple games (e.g. Earth 2150). (cherry picked from commit a455dd53d5af144aae70263d9f4e798a305ab775) --- dlls/winegstreamer/quartz_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index dfbe62a7a81..889a9223d3e 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -343,7 +343,7 @@ unsigned int wg_format_get_max_size(const struct wg_format *format) { case WG_MAJOR_TYPE_VIDEO: { - unsigned int width = format->u.video.width, height = format->u.video.height; + unsigned int width = format->u.video.width, height = abs(format->u.video.height); switch (format->u.video.format) { From 9ed4fbd97d62bc8182eff9ff716098f5dcb96939 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 401/758] winegstreamer: In video_processor, activate a videoflip converter. The app I'm considering opens a video_processor on its own, with a NV12 format on input and a ARGB32 format on output. Tested on Windows: the samples are flipped vertically. While Wine keeps them untouched. So added a videoflip in the video processor to be activated when needed. CW-Bug-Id: 21028 Signed-off-by: Eric Pouech --- dlls/winegstreamer/color_convert.c | 28 ++++++++++++++++++++++++++++ dlls/winegstreamer/wg_transform.c | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/dlls/winegstreamer/color_convert.c b/dlls/winegstreamer/color_convert.c index 0eaddc687ee..ed591c8e669 100644 --- a/dlls/winegstreamer/color_convert.c +++ b/dlls/winegstreamer/color_convert.c @@ -363,6 +363,7 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM struct color_convert *impl = impl_from_IMFTransform(iface); GUID major, subtype; UINT64 frame_size; + UINT32 stride; HRESULT hr; ULONG i; @@ -392,6 +393,19 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM IMFMediaType_Release(impl->input_type); impl->input_type = NULL; } + if (FAILED(IMFMediaType_GetUINT32(impl->input_type, &MF_MT_DEFAULT_STRIDE, &stride))) + { + if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, frame_size >> 32, (LONG *)&stride))) + { + IMFMediaType_Release(impl->input_type); + impl->input_type = NULL; + } + if (FAILED(hr = IMFMediaType_SetUINT32(impl->input_type, &MF_MT_DEFAULT_STRIDE, abs((INT32)stride)))) + { + IMFMediaType_Release(impl->input_type); + impl->input_type = NULL; + } + } if (impl->output_type && FAILED(hr = try_create_wg_transform(impl))) { @@ -411,6 +425,7 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF struct color_convert *impl = impl_from_IMFTransform(iface); GUID major, subtype; UINT64 frame_size; + UINT32 stride; HRESULT hr; ULONG i; @@ -440,6 +455,19 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF IMFMediaType_Release(impl->output_type); impl->output_type = NULL; } + if (FAILED(IMFMediaType_GetUINT32(impl->output_type, &MF_MT_DEFAULT_STRIDE, &stride))) + { + if (FAILED(hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, frame_size >> 32, (LONG *)&stride))) + { + IMFMediaType_Release(impl->output_type); + impl->output_type = NULL; + } + if (FAILED(hr = IMFMediaType_SetUINT32(impl->output_type, &MF_MT_DEFAULT_STRIDE, abs((INT32)stride)))) + { + IMFMediaType_Release(impl->output_type); + impl->output_type = NULL; + } + } if (impl->input_type && FAILED(hr = try_create_wg_transform(impl))) { diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 18ba562ab22..41f49351b0a 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -52,6 +52,9 @@ struct wg_transform guint input_max_length; GstAtomicQueue *input_queue; + bool input_is_flipped; + GstElement *video_flip; + guint output_plane_align; struct wg_sample *output_wg_sample; GstAtomicQueue *output_queue; @@ -276,6 +279,11 @@ static struct wg_sample *transform_request_sample(gsize size, void *context) return InterlockedExchangePointer((void **)&transform->output_wg_sample, NULL); } +static bool wg_format_video_is_flipped(const struct wg_format *format) +{ + return format->major_type == WG_MAJOR_TYPE_VIDEO && (format->u.video.height < 0); +} + NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; @@ -392,6 +400,12 @@ NTSTATUS wg_transform_create(void *args) break; case WG_MAJOR_TYPE_VIDEO: + if (!(transform->video_flip = create_element("videoflip", "base")) + || !append_element(transform->container, transform->video_flip, &first, &last)) + goto out; + transform->input_is_flipped = wg_format_video_is_flipped(&input_format); + if (transform->input_is_flipped != wg_format_video_is_flipped(&output_format)) + gst_util_set_object_arg(G_OBJECT(transform->video_flip), "method", "vertical-flip"); if (!(element = create_element("videoconvert", "base")) || !append_element(transform->container, element, &first, &last)) goto out; @@ -519,6 +533,15 @@ NTSTATUS wg_transform_set_output_format(void *args) transform->setting_output_format = true; + if (transform->video_flip) + { + const char *value; + if (transform->input_is_flipped != wg_format_video_is_flipped(format)) + value = "vertical-flip"; + else + value = "none"; + gst_util_set_object_arg(G_OBJECT(transform->video_flip), "method", value); + } if (!gst_pad_push_event(transform->my_sink, gst_event_new_reconfigure())) { GST_ERROR("Failed to reconfigure transform %p.", transform); From a77454f07b99b0cca489750c7a0b0c0d3334a767 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 402/758] winegstreamer: Add a second videoconvert before the videoflip. videoflip can't handle 15/16-bit RGB. Fixes video playback in multiple games (e.g. Hard Truck 2, Firestarter). (cherry picked from commit dd20b895713b4e9384267765ee4181d32855472e) --- dlls/winegstreamer/wg_transform.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 41f49351b0a..dc5fd51e998 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -400,6 +400,9 @@ NTSTATUS wg_transform_create(void *args) break; case WG_MAJOR_TYPE_VIDEO: + if (!(element = create_element("videoconvert", "base")) + || !append_element(transform->container, element, &first, &last)) + goto out; if (!(transform->video_flip = create_element("videoflip", "base")) || !append_element(transform->container, transform->video_flip, &first, &last)) goto out; From 5f0a4b61df3393ea67c4754144dc430d9eff5988 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 403/758] winegstreamer: Don't force top-down orientation when changing output format in video_decoder. (cherry picked from commit c73e8b8551947e9547331ff5b72b206981c5f645) --- dlls/winegstreamer/video_decoder.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 1fdabd46b96..04d175c7910 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -310,8 +310,6 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF { mf_media_type_to_wg_format(decoder->output_type, &output_format); - output_format.u.video.width = frame_size >> 32; - output_format.u.video.height = (UINT32)frame_size; output_format.u.video.fps_d = 0; output_format.u.video.fps_n = 0; From 48ac809bb42da1aa2bb7f71f46b4a2ac81833dbe Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 404/758] ir50_32: Let video_decoder flip the video instead of doing it manually. Fixes upside-down videos in multiple games (e.g. Hard Truck 2, Firestarter). (cherry picked from commit 79ce998e794189b4f9cd11916e1dbc0d7508ca75) --- dlls/ir50_32/ir50.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 87807aba25f..280983e58d8 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -145,6 +145,7 @@ static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPB IMFMediaType *input_type, *output_type; const GUID *output_subtype; LRESULT r = ICERR_INTERNAL; + unsigned int stride; TRACE("ICM_DECOMPRESS_BEGIN %p %p %p\n", decoder, in, out); @@ -158,6 +159,10 @@ static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPB else return ICERR_BADFORMAT; + stride = (out->bmiHeader.biWidth + 3) & ~3; + if (out->bmiHeader.biHeight >= 0) + stride = -stride; + if ( FAILED(MFCreateMediaType( &input_type )) ) return ICERR_INTERNAL; @@ -182,6 +187,8 @@ static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPB output_type, &MF_MT_FRAME_SIZE, make_uint64( out->bmiHeader.biWidth, abs(out->bmiHeader.biHeight) ) )) ) goto done; + if ( FAILED(IMFMediaType_SetUINT32( output_type, &MF_MT_DEFAULT_STRIDE, stride)) ) + goto done; if ( FAILED(IMFTransform_SetInputType( decoder, 0, input_type, 0 )) || FAILED(IMFTransform_SetOutputType( decoder, 0, output_type, 0 )) ) @@ -250,17 +257,13 @@ static LRESULT IV50_Decompress( IMFTransform *decoder, ICDECOMPRESS *icd, DWORD { LONG width = icd->lpbiOutput->biWidth * (icd->lpbiOutput->biBitCount / 8); LONG height = abs( icd->lpbiOutput->biHeight ); - LONG data_stride = (width + 3) & ~3; - LONG out_stride = icd->lpbiOutput->biHeight >= 0 ? -data_stride : data_stride; - BYTE *output_start = (BYTE *)icd->lpOutput; - - if (out_stride < 0) - output_start += (height - 1) * abs(out_stride); + LONG stride = (width + 3) & ~3; + BYTE *output = (BYTE *)icd->lpOutput; if ( FAILED(IMFMediaBuffer_Lock( out_buf, &data, NULL, NULL ))) goto done; - MFCopyImage( output_start, out_stride, data, data_stride, width, height ); + MFCopyImage( output, stride, data, stride, width, height ); IMFMediaBuffer_Unlock( out_buf ); r = ICERR_OK; From 94d6ad0e6c0fe133920fc1e0f8861c7ef8646cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 22:59:39 +0200 Subject: [PATCH 405/758] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_FLUSH for h264 decoder." This reverts commit 6fc8c46aab46ad5e3ccc1019a44288f3143279e9. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/mf/tests/transform.c | 19 ++++++++++++++++--- dlls/winegstreamer/aac_decoder.c | 2 +- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/h264_decoder.c | 4 +--- dlls/winegstreamer/main.c | 3 +-- dlls/winegstreamer/resampler.c | 2 +- dlls/winegstreamer/unixlib.h | 1 - dlls/winegstreamer/video_processor.c | 2 +- dlls/winegstreamer/wg_transform.c | 14 +------------- dlls/winegstreamer/wma_decoder.c | 2 +- 10 files changed, 24 insertions(+), 27 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 2cf36bf4b38..05504cac9b3 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -6567,9 +6567,6 @@ static void test_h264_reuse(void) ok(hr == S_OK, "got %#lx\n", hr); IMFSample_Release(output_sample); - hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_FLUSH, 0); - ok(hr == S_OK, "got %#lx\n", hr); - IMFSample_Release(input_sample); load_resource(L"stream2.bin", &data1, &data1_len); @@ -6600,6 +6597,22 @@ static void test_h264_reuse(void) i++; } trace("i %d.\n", i); + ok(hr == S_OK, "got %#lx\n", hr); + while (hr == S_OK) + { + IMFSample_Release(output_sample); + output_sample = create_sample(NULL, width * height * 3 / 2); + hr = check_mft_process_output(transform, output_sample, &output_status); + } + while (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) + { + hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); + ok(hr == S_OK, "got %#lx\n", hr); + IMFSample_Release(input_sample); + input_sample = next_h264_sample(&data1, &data1_len); + + hr = check_mft_process_output(transform, output_sample, &output_status); + } ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "got %#lx\n", hr); diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 01f07e6b713..3be7e91147f 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -542,7 +542,7 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ return MF_E_TRANSFORM_TYPE_NOT_SET; if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform, FALSE); + return wg_transform_drain(decoder->wg_transform); FIXME("Ignoring message %#x.\n", message); diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 7103793cf50..81aefe4234f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -105,7 +105,7 @@ struct wg_transform *wg_transform_create(const struct wg_format *input_format, void wg_transform_destroy(struct wg_transform *transform); bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_format *format); bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); -HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush); +HRESULT wg_transform_drain(struct wg_transform *transform); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index c5600389be7..a1d70eb5733 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -590,9 +590,7 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ return MF_E_TRANSFORM_TYPE_NOT_SET; if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform, FALSE); - if (message == MFT_MESSAGE_COMMAND_FLUSH) - return wg_transform_drain(decoder->wg_transform, TRUE); + return wg_transform_drain(decoder->wg_transform); FIXME("Ignoring message %#x.\n", message); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index b0afca51d74..df7f123aaee 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -415,12 +415,11 @@ bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_fo return !WINE_UNIX_CALL(unix_wg_transform_set_output_format, ¶ms); } -HRESULT wg_transform_drain(struct wg_transform *transform, BOOL flush) +HRESULT wg_transform_drain(struct wg_transform *transform) { struct wg_transform_drain_params params = { .transform = transform, - .flush = flush, }; TRACE("transform %p.\n", transform); diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 4c8d27856f9..c4efd5fb82c 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -513,7 +513,7 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ return MF_E_TRANSFORM_TYPE_NOT_SET; if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(impl->wg_transform, FALSE); + return wg_transform_drain(impl->wg_transform); FIXME("Ignoring message %#x.\n", message); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 79f151bd965..175538ad147 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -335,7 +335,6 @@ struct wg_transform_get_status_params struct wg_transform_drain_params { struct wg_transform *transform; - BOOL flush; }; struct wg_source_create_params diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 03186b36d9d..d8ef51b7bf2 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -513,7 +513,7 @@ static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_ME return MF_E_TRANSFORM_TYPE_NOT_SET; if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(impl->wg_transform, FALSE); + return wg_transform_drain(impl->wg_transform); FIXME("Ignoring message %#x.\n", message); diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index dc5fd51e998..63d27b8bc32 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -573,16 +573,13 @@ NTSTATUS wg_transform_drain(void *args) struct wg_transform_drain_params *params = args; struct wg_transform *transform = params->transform; GstBuffer *input_buffer; - GstSample *sample; GstFlowReturn ret; GstEvent *event; while ((input_buffer = gst_atomic_queue_pop(transform->input_queue))) { - if (params->flush) - gst_buffer_unref(input_buffer); - else if ((ret = gst_pad_push(transform->my_src, input_buffer))) + if ((ret = gst_pad_push(transform->my_src, input_buffer))) { GST_ERROR("Failed to push transform input, error %d", ret); return S_OK; @@ -612,15 +609,6 @@ NTSTATUS wg_transform_drain(void *args) return MF_E_STREAM_ERROR; } - if (params->flush) - { - if (transform->output_sample) - gst_sample_unref(transform->output_sample); - while ((sample = gst_atomic_queue_pop(transform->output_queue))) - gst_sample_unref(sample); - transform->output_sample = NULL; - } - return S_OK; } diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index fa649fea63b..9354822e45c 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -530,7 +530,7 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ return MF_E_TRANSFORM_TYPE_NOT_SET; if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform, FALSE); + return wg_transform_drain(decoder->wg_transform); FIXME("Ignoring message %#x.\n", message); From 54622da4850cf2047fbd88d3b93644507147aeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 22:59:40 +0200 Subject: [PATCH 406/758] Revert "winegstreamer: Return gst_pad_query_default() when processing GST_QUERY_CAPS for input stream format change." This reverts commit a76a0c4b0ca3cbf892b3f377ed2923a70b510b1f. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/mf/tests/resource.rc | 19 ---- dlls/mf/tests/stream1.bin | Bin 7174 -> 0 bytes dlls/mf/tests/stream2.bin | Bin 4416 -> 0 bytes dlls/mf/tests/transform.c | 168 ------------------------------ dlls/winegstreamer/wg_transform.c | 7 -- 5 files changed, 194 deletions(-) delete mode 100644 dlls/mf/tests/stream1.bin delete mode 100644 dlls/mf/tests/stream2.bin diff --git a/dlls/mf/tests/resource.rc b/dlls/mf/tests/resource.rc index 58654b93c33..25768d21983 100644 --- a/dlls/mf/tests/resource.rc +++ b/dlls/mf/tests/resource.rc @@ -63,25 +63,6 @@ mp3decdata.bin RCDATA mp3decdata.bin /* @makedep: h264data.bin */ h264data.bin RCDATA h264data.bin - -/* Generated with: - * gst-launch-1.0 videotestsrc num-buffers=60 pattern=smpte100 ! \ - * video/x-raw,format=I420,width=1920,height=1080,framerate=30000/1001 ! \ - * videoflip method=clockwise ! videoconvert ! \ - * x264enc ! filesink location=dlls/mf/tests/h264data.bin - */ -/* @makedep: stream1.bin */ -stream1.bin RCDATA stream1.bin - -/* Generated with: - * gst-launch-1.0 videotestsrc num-buffers=60 pattern=smpte100 ! \ - * video/x-raw,format=I420,width=1280,height=720,framerate=30000/1001 ! \ - * videoflip method=clockwise ! videoconvert ! \ - * x264enc ! filesink location=dlls/mf/tests/h264data.bin - */ -/* @makedep: stream2.bin */ -stream2.bin RCDATA stream2.bin - /* Generated from running the tests on Windows */ /* @makedep: nv12frame.bmp */ nv12frame.bmp RCDATA nv12frame.bmp diff --git a/dlls/mf/tests/stream1.bin b/dlls/mf/tests/stream1.bin deleted file mode 100644 index f16bdaec31cd097310c31af70e15e30f9175b3e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7174 zcmb_f4LDS38$L4#vC66}#hw)vi}~@hewvsSDTJ_V+n?#oILt7AW(LFdGb`z5w^F_= zkx8{hY>{k~jnc-pwQ7@AQmIW}M7Ba@@x5opOvCE>Fs{or-uFGvdq2-}KhHVu{X!6g z)13){->3*^QBjlUQjOa0t5-`TlKl|04u;J94IfM+S7QdujwdHLY>IBmf6m=vL!2@4=U_b~6@?K`=2va}t z0+tad13d8J@e|o-qzEPxi4JyTBAE;pnIaM2jX+39NU)D*MW8%3!nNlKq6nB5d!~rP z1~xpth{fXy-CzbnM;H_mEI=bE4sZlYXY&{_6p|a!jR+%LgqL<$fNDg z5htQJ60tA^2*#-Z6*9qg0fVMY7ke&XP#AOp%%KBfv9#zcU=I=`6CGV(Y$W2bu@b?t zSaKAiy)zi{F-Nlhh?wH+3KoPSluvPlmz@Rl~Oi*<$ zC>0#ZVzW_s8*~=%1Y$yBK_g*p0ZBmTqgzrSlcB-B>mHX+bo9BFJ;?7nkh_59 z6=#LZ&bZL&25dAGfBJ?iM2;z5A&%W?*b>eF(wYakRBIBUP)O$`ZZ@<)qLqf52N5KC z)p!`%f*H+%{~k11p95KJr@idmHFR&Kw5Td4_n+$}by2x91)UIskBZNR z9I%HMbyVrTI$^vykd(7c^UR|?Gn4*gi`g=0lZK&r)?G$NPSUSD_p`k#z7|A$;}Wl# z?2FBGCm6=)m~~Rx?N9wW*!W9>gEv9P`j-LI(3~X0mfCM;-ysxHb_JQGi^_kKyT!-x z0>UgJZkEkdiDNY0IG7l*Mel5JN==&kxvhUDmGWH9r@!a!{$eJr>Vrl5@J{Y8);em3 zT)JL*kaww9ymw`R!+tChoRPM;=TiMQe2f5{Xb`|&msVW9Gvm`A6H1E~|J4sWfUPL0Z`c=J=%7PM?b>7>8Z-^8`_2m1w z@5b7wwMU7L%SA*~!dOOQg{9(8wnZ~X1&9%?2GBkkkep)X@_Z^Y%U{QCXdFb34oML< zu22~oH8<7U(AH>ZvRP{2XDG37xj(4Gmpo0Zthn4G>hL8eUd~q$;EO$_d|mRMJ!O17 zR);Uu{_2=2zPg9`A`A^Sepfu%uHaCWO+l3!OdVlTJ9P+CX)q~aiU`YgoEpMBRR~)( zg@nbbLzqg3Nrh8In0S~lr=h`Hhr=h^Va|Cfgk=E2=1l$*25pxp!En+t5jAkKQEA|e zRgg|aQ+bm{g0_DngOYv5vMa|!nqI}>mU!4i+Og(ln3EN=>(12aqHbz|0*i9SrR;-xx^r(PX9oZeeV@?UH2RVudj2RUmeibUFvRR zTf3p(CbGD5Q|IsI)@HY^Whj8xR4cinN^gM@nyOJk-3lcbPWnnj-Kp@RIP>0i7XEwP z@}3G*e>7D(d8qdcWl-`O-W#iic+Y3GLy!?6sV1nfZU3c5Ek7B4VO(uNxw~}d<&ODU zb?+B^Sz920{LE7LEt`6DG+^jR-<5#d{XZ2bc~oS$a&1u6Kx2LN%JrQso7u@{0(5<% zPxoBbDYg8|Jo%wn@M)LKz{D>}+oCP}HgyOrjZHTP8*j|k8~Alg>$0q_;7_}zYu4(2 z_(L*j^&aEnRS^sIPg-q+O{zTNZIinLw_WyG9Mw!4uzr7zcOrdL{es3z&*E+`xyaXg zn9x$@Up$SwuIT&BljiALPo#dX-B`CeRF{1+diYLxFqjZSFy)1eQjGSi8N)<35vEL3h z3j5#F5kZGbF3xMWV2TG+^m8ySStl{z>Mt5u1q^B>v=wHRyu`uuGjf(Tzev2Rv zBK<4H^q!A@_VJA@yJ{OO;vIHG6XR2KSPkPa)EsWj}0$);VXE97n$GYSuZXchAuL>49Z! zal4ZLn$d9VK|=*(wd1@C_x$S=TpAS8-*&ibU2~}7B*W<*>FJo|mbU3e+Q5QToy9(Z zP;#H$;gWYgvR&(O@sH?7sUJSx?GfM5Eg3GA$=@oa!7e8gD9!Yu0jll6#dXn8-#!vS2c(X)@8T*=9`&Oi%nD*5+MeLy2R;cDazo@p% zXD{YA;%m+-rHpEj-%jLJ1{a<2J643Q>}u7cH~Y+3M=##<`Ne0ZlErow{8o^#3Ie`y z^F7o$(O}_w{+|{4E_+uiyt3q-odJetkm`5cgc?l_#!b#AY&^PGe93SxU0Y)+>hn(+ zL;c}gU%2(RIbFjpf*O$C`oVva>+AifZseQ(GIz56q(5?4D; N35S!uR*$^${{cQ_{{#R4 diff --git a/dlls/mf/tests/stream2.bin b/dlls/mf/tests/stream2.bin deleted file mode 100644 index 265878cced89f3a465b72e123c8e93a7f49ef1e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4416 zcmeHJdsGzH9iHVSqM+u~AezFMLXgN~_Q7jqPFM*MkEDmFh)L9Oc6OE>o!uE`2H4do zV0>1>X*D3?aTl!-BocC{#2O#qqX`;nTQv&!2qtMNQNWa(ps}*O!?Md_VOjoa|L8fK z+26hQyWjnO-+lamAV@NCDg?gS8YtvwTk465wpx2{rFNeFt}X3&S!65R_N(q5qbyDVN5KTck+2kmoE%Avh1EDoDi8z=q(uWj z%2X#Ta-cOyFvqK&3E8YEe0?A=NZPtVN|tM2Wz-0jEtoi6Tb15;01p za#&BII?@R9xoQx_!z^zCQ^6PGG^jKh0h7Q7)>B4OgNck%FazUoLpF&@<6uI^F?t*W zQ>C!Lku**5AWASMXb1rWM2;RsfCOB#jxms^Od2Vb!djdcFqU6Su_D4oki%k(R?CwD z8YP1T9S3Frq6C;`n6K^dY*fW=5)XmN|+95Fc*iH-vyD=swfiyP6n zM9{$tB#SEGJarzX$Bjb_Vuw0c%TwzBxlEP-gQJ1zz^)s>R>4||rb*FPiUqq3P8L`R zXu@;AK@yJzR0vK!xqUM<)7sy6?RZOl*AnKFN!5J^%`agoxnYuhWf!`X0OL9M*ST~P z@lH4Nx8h@B$C4($^1!K!&b~S!ZSL8v{?L^^=7-X*M|M1hKmA>E$K>;7m52X?o78`u}n;^*l;hdsl zzo;BqcJ9{LttWj`o-{vs{obiPmZUfDz^Yj5%I^!#b(k7X9P&n_U2^m4WNbx2R_1T` z+6#BtCUUtb+Y`;Z``$Na|CzpGh|H19V9-odrC%?T^<4h?`nKjTk8xiu&YI_S{^gsQ z{bAQ7uXOJ`vhrzAFe-=yQx;9@yL55=E_KJqbdnv4NjG*EAhM0Z;u{ZmdgVM0?mn3C z!WNIK$!~h0-+d^jI9RfG{~=4={vpzoE33{Vg@pbIWN!EQnPAK21KX>Z){;eh{E3#O zDc&=(wE62>GS87O_E$aXd3^C+z~v6p=G(F^;zqJa(tBz9z0cqMw)~GHGfG|z_tfmk zzYtKIShS#Svhv8TLqB;ZdfoAr7MD&;4wn)ZVODly}N$}&1M zveF4ktZ@lFFaWj5)h(=6>Gr1k##&vAKI@=YnYX4+i=1g4UCS`pAGnrn(;}K7F;}^k zZOhOm`zVl2yCJ*DN%p1G>F&$!bS>Ma#U}f{`?4RpDci|yOTai}TcQM2oqHQ&iCNvvnyszs_MKMU>92woT`!qk-$3_RF8?{QBctIE=F&E(V5?KA!J^S+dU<1t*0q~D3=iTe(9JnN8W#Kq$s zQSbZwef8)6iVyqZeAkvAm%TlEPw$j={(NA~^zUUUu^$x?nayuMy8TVHPgg|z?S}M^ zA!v4c=hwY489Ti0CflVce)6aIMpdiW_S@@_r!of*nNB!;o%|%H*wf24~mnkcgN|{m_c!J1$DX!6=HF6=aqN5z@Rvd z2VS_TuCUYReD-P*sJC;w0%U-uCO~(m&Ar_5S+laktL3VyR?(PyzC^DoMKT zz^dPe%HO*8eC&!Xjqlx278> 32; - width = frame_size & 0xffffffff; - ok(width == 1920, "got %u.\n", width); - ok(height == 1088, "got %u.\n", height); - IMFMediaType_Release(type); - - output_sample = create_sample(NULL, 0x1000); - hr = check_mft_process_output(transform, output_sample, &output_status); - ok(hr == E_FAIL, "got %#lx\n", hr); - IMFSample_Release(output_sample); - - output_sample = create_sample(NULL, width * height * 3 / 2); - hr = check_mft_process_output(transform, output_sample, &output_status); - ok(hr == S_OK, "got %#lx\n", hr); - IMFSample_Release(output_sample); - - IMFSample_Release(input_sample); - - load_resource(L"stream2.bin", &data1, &data1_len); - i = 0; - input_sample = next_h264_sample(&data1, &data1_len); - while (1) - { - output_sample = create_sample(NULL, width * height * 3 / 2); - hr = check_mft_process_output(transform, output_sample, &output_status); - if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT) break; - - ok(output_status == 0, "got output[0].dwStatus %#lx\n", output_status); - hr = IMFSample_GetTotalLength(output_sample, &length); - ok(hr == S_OK, "GetTotalLength returned %#lx\n", hr); - ok(length == 0, "got length %lu\n", length); - IMFSample_Release(output_sample); - - hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); - ok(hr == S_OK, "got %#lx\n", hr); - IMFSample_Release(input_sample); - input_sample = next_h264_sample(&data1, &data1_len); - - hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); - ok(hr == S_OK, "got %#lx\n", hr); - IMFSample_Release(input_sample); - input_sample = next_h264_sample(&data1, &data1_len); - - i++; - } - trace("i %d.\n", i); - ok(hr == S_OK, "got %#lx\n", hr); - while (hr == S_OK) - { - IMFSample_Release(output_sample); - output_sample = create_sample(NULL, width * height * 3 / 2); - hr = check_mft_process_output(transform, output_sample, &output_status); - } - while (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) - { - hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); - ok(hr == S_OK, "got %#lx\n", hr); - IMFSample_Release(input_sample); - input_sample = next_h264_sample(&data1, &data1_len); - - hr = check_mft_process_output(transform, output_sample, &output_status); - } - - ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "got %#lx\n", hr); - - hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &type); - ok(hr == S_OK, "got %#lx\n", hr); - IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size); - ok(hr == S_OK, "got %#lx\n", hr); - height = frame_size >> 32; - width = frame_size & 0xffffffff; - ok(width == 1280, "got %u.\n", width); - ok(height == 720, "got %u.\n", height); - IMFMediaType_Release(type); - - IMFSample_Release(output_sample); - IMFSample_Release(input_sample); - - IMFTransform_Release(transform); -failed: - CoUninitialize(); -} - START_TEST(transform) { init_functions(); @@ -6652,5 +6485,4 @@ START_TEST(transform) test_color_convert(); test_video_processor(); test_mp3_decoder(); - test_h264_reuse(); } diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 63d27b8bc32..2d9eb57fa6d 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -62,7 +62,6 @@ struct wg_transform bool output_caps_changed; GstCaps *output_caps; bool broken_timestamps; - bool setting_output_format; }; static void align_video_info_planes(gsize plane_align, GstVideoInfo *info, GstVideoAlignment *align) @@ -180,9 +179,6 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery GstCaps *caps, *filter, *temp; gchar *str; - if (!transform->setting_output_format) - return gst_pad_query_default(pad, parent, query); - gst_query_parse_caps(query, &filter); caps = gst_caps_ref(transform->output_caps); @@ -222,7 +218,6 @@ static gboolean transform_sink_event_cb(GstPad *pad, GstObject *parent, GstEvent { GstCaps *caps; - transform->setting_output_format = false; gst_event_parse_caps(event, &caps); transform->output_caps_changed = transform->output_caps_changed @@ -534,8 +529,6 @@ NTSTATUS wg_transform_set_output_format(void *args) gst_caps_unref(transform->output_caps); transform->output_caps = caps; - transform->setting_output_format = true; - if (transform->video_flip) { const char *value; From 7fddf6c24e352fbd7859cb55cc0e96bd351f59eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:01:29 +0200 Subject: [PATCH 407/758] Revert "winegstreamer: Don't pre-check sample size in wg_transform_read_mf()." This reverts commit 6af55bf56157767b8c8d6c8a4415aa45589be6f2. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/wg_sample.c | 5 ++++ dlls/winegstreamer/wg_transform.c | 48 +++++++++++-------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/dlls/winegstreamer/wg_sample.c b/dlls/winegstreamer/wg_sample.c index 1278ccf7347..d20583bdb22 100644 --- a/dlls/winegstreamer/wg_sample.c +++ b/dlls/winegstreamer/wg_sample.c @@ -353,6 +353,11 @@ HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample, return hr; wg_sample->size = 0; + if (wg_sample->max_size < sample_size) + { + wg_sample_release(wg_sample); + return MF_E_BUFFERTOOSMALL; + } if (FAILED(hr = wg_transform_read_data(transform, wg_sample, format))) { diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 2d9eb57fa6d..c8f292e60d5 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -654,19 +654,19 @@ NTSTATUS wg_transform_push_data(void *args) return STATUS_SUCCESS; } -static NTSTATUS copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_align, +static bool copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_align, struct wg_sample *sample, gsize *total_size) { - NTSTATUS status = STATUS_UNSUCCESSFUL; GstVideoFrame src_frame, dst_frame; GstVideoInfo src_info, dst_info; GstVideoAlignment align; GstBuffer *dst_buffer; + bool ret = false; if (!gst_video_info_from_caps(&src_info, caps)) { GST_ERROR("Failed to get video info from caps."); - return STATUS_UNSUCCESSFUL; + return false; } dst_info = src_info; @@ -675,14 +675,14 @@ static NTSTATUS copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_ if (sample->max_size < dst_info.size) { GST_ERROR("Output buffer is too small."); - return STATUS_BUFFER_TOO_SMALL; + return false; } if (!(dst_buffer = gst_buffer_new_wrapped_full(0, sample->data, sample->max_size, 0, sample->max_size, 0, NULL))) { GST_ERROR("Failed to wrap wg_sample into GstBuffer"); - return STATUS_UNSUCCESSFUL; + return false; } gst_buffer_set_size(dst_buffer, dst_info.size); *total_size = sample->size = dst_info.size; @@ -695,9 +695,7 @@ static NTSTATUS copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_ GST_ERROR("Failed to map destination frame."); else { - if (gst_video_frame_copy(&dst_frame, &src_frame)) - status = STATUS_SUCCESS; - else + if (!(ret = gst_video_frame_copy(&dst_frame, &src_frame))) GST_ERROR("Failed to copy video frame."); gst_video_frame_unmap(&dst_frame); } @@ -705,16 +703,16 @@ static NTSTATUS copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_ } gst_buffer_unref(dst_buffer); - return status; + return ret; } -static NTSTATUS copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample *sample, +static bool copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample *sample, gsize *total_size) { GstMapInfo info; if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) - return STATUS_UNSUCCESSFUL; + return false; if (sample->max_size >= info.size) sample->size = info.size; @@ -731,16 +729,15 @@ static NTSTATUS copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample * gst_buffer_resize(buffer, sample->size, -1); *total_size = info.size; - return STATUS_SUCCESS; + return true; } static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsize plane_align, struct wg_sample *sample) { + bool ret, needs_copy; gsize total_size; - bool needs_copy; GstMapInfo info; - NTSTATUS status; if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) { @@ -751,24 +748,18 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsi needs_copy = info.data != sample->data; gst_buffer_unmap(buffer, &info); - if (!needs_copy) - { + if ((ret = !needs_copy)) total_size = sample->size = info.size; - status = STATUS_SUCCESS; - } + else if (stream_type_from_caps(caps) == GST_STREAM_TYPE_VIDEO) + ret = copy_video_buffer(buffer, caps, plane_align, sample, &total_size); else - { - if (stream_type_from_caps(caps) == GST_STREAM_TYPE_VIDEO) - status = copy_video_buffer(buffer, caps, plane_align, sample, &total_size); - else - status = copy_buffer(buffer, caps, sample, &total_size); - } + ret = copy_buffer(buffer, caps, sample, &total_size); - if (status) + if (!ret) { GST_ERROR("Failed to copy buffer %p", buffer); sample->size = 0; - return status; + return STATUS_UNSUCCESSFUL; } if (GST_BUFFER_PTS_IS_VALID(buffer)) @@ -905,11 +896,6 @@ NTSTATUS wg_transform_read_data(void *args) if ((status = read_transform_output_data(output_buffer, output_caps, transform->output_plane_align, sample))) { - if (status == STATUS_BUFFER_TOO_SMALL) - { - status = 0; - params->result = E_FAIL; - } wg_allocator_release_sample(transform->allocator, sample, false); return status; } From 9450dc5e1be2f4222c24c357b3304a44559a2020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:07:55 +0200 Subject: [PATCH 408/758] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for wma decoder." This reverts commit 051fd7938d4e0a59872ab18cae9fbe957c889a62. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/wma_decoder.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 9354822e45c..10a41a0c92f 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -522,18 +522,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct wma_decoder *decoder = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!decoder->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); return S_OK; } From 124475ff4e41efb6ab7ad1cb2f9cd514ea6e3d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:07:57 +0200 Subject: [PATCH 409/758] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for video processor." This reverts commit 6b04b251a08801467ffc36da2afd4f02c1ac5ea4. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/video_processor.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index d8ef51b7bf2..21bc46b0bb3 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -505,18 +505,7 @@ static HRESULT WINAPI video_processor_ProcessEvent(IMFTransform *iface, DWORD id static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct video_processor *impl = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!impl->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(impl->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %#Ix stub!\n", iface, message, param); return S_OK; } From 0b8e00ef596e7486246fe9aa5f4cade1071823e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:08:00 +0200 Subject: [PATCH 410/758] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for resampler." This reverts commit 0799ee446e37cbc69c0078f952b3b89552f39465. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/resampler.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index c4efd5fb82c..88e9727ff21 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -505,18 +505,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct resampler *impl = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!impl->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(impl->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); return S_OK; } From 93b2caba7c16929f4d5b0557104b12899a8da27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:08:10 +0200 Subject: [PATCH 411/758] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for aac decoder." This reverts commit 37e94ecce5752e10842601d8a30dd971ad1348a7. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/mf/tests/transform.c | 13 ++----------- dlls/winegstreamer/aac_decoder.c | 13 +------------ 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 476d385f862..acd9439b6b1 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -2470,17 +2470,8 @@ static void test_aac_decoder_subtype(const struct attribute_desc *input_type_des hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); - flags = 0xdeadbeef; - hr = IMFTransform_GetInputStatus(transform, 0, &flags); - ok(hr == S_OK, "Got %#lx\n", hr); - ok(!flags, "Got flags %#lx.\n", flags); - if (0) - { - /* This is fine on Windows but currently MFT_MESSAGE_COMMAND_DRAIN removes input sample from the queue - * and makes next _ProcessInput succeed on Wine breaking the tests below. */ - hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); - ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); - } + hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); + ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 3be7e91147f..d79ede69c9d 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -534,18 +534,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct aac_decoder *decoder = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); - - if (!decoder->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); return S_OK; } From 69b19a4cb720bdcb7682fa6b8d1bf52f08943239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:08:58 +0200 Subject: [PATCH 412/758] Revert "winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for h264 transform." This reverts commit fa28d88d4ffe78448fbdd27b37633628a37f7bf5. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/mf/tests/transform.c | 1 + dlls/winegstreamer/gst_private.h | 1 - dlls/winegstreamer/h264_decoder.c | 13 +------- dlls/winegstreamer/main.c | 12 -------- dlls/winegstreamer/unix_private.h | 1 - dlls/winegstreamer/unixlib.h | 6 ---- dlls/winegstreamer/wg_parser.c | 1 - dlls/winegstreamer/wg_transform.c | 49 ------------------------------- 8 files changed, 2 insertions(+), 82 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index acd9439b6b1..d3dca90cdf7 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -3700,6 +3700,7 @@ static void test_h264_decoder(void) ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); } ok(i == 2, "got %lu iterations\n", i); + todo_wine ok(h264_encoded_data_len == 1180, "got h264_encoded_data_len %lu\n", h264_encoded_data_len); ok(hr == MF_E_TRANSFORM_STREAM_CHANGE, "ProcessOutput returned %#lx\n", hr); ok(output_status == MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE, "got output[0].dwStatus %#lx\n", output_status); diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 81aefe4234f..e2c32d12b64 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -105,7 +105,6 @@ struct wg_transform *wg_transform_create(const struct wg_format *input_format, void wg_transform_destroy(struct wg_transform *transform); bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_format *format); bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); -HRESULT wg_transform_drain(struct wg_transform *transform); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index a1d70eb5733..3088f2f7837 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -582,18 +582,7 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - struct h264_decoder *decoder = impl_from_IMFTransform(iface); - - TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); - - if (!decoder->wg_transform) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (message == MFT_MESSAGE_COMMAND_DRAIN) - return wg_transform_drain(decoder->wg_transform); - - FIXME("Ignoring message %#x.\n", message); - + FIXME("iface %p, message %#x, param %Ix stub!\n", iface, message, param); return S_OK; } diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index df7f123aaee..5cbaf49e38a 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -415,18 +415,6 @@ bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_fo return !WINE_UNIX_CALL(unix_wg_transform_set_output_format, ¶ms); } -HRESULT wg_transform_drain(struct wg_transform *transform) -{ - struct wg_transform_drain_params params = - { - .transform = transform, - }; - - TRACE("transform %p.\n", transform); - - return WINE_UNIX_CALL(unix_wg_transform_drain, ¶ms); -} - struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]) { diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 1f4ebf0a2df..025ef83ed99 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -66,7 +66,6 @@ extern NTSTATUS wg_transform_set_output_format(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_push_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_read_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_get_status(void *args) DECLSPEC_HIDDEN; -extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; /* wg_source.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 175538ad147..660a32374e2 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -332,11 +332,6 @@ struct wg_transform_get_status_params UINT32 accepts_input; }; -struct wg_transform_drain_params -{ - struct wg_transform *transform; -}; - struct wg_source_create_params { const char *url; @@ -414,7 +409,6 @@ enum unix_funcs unix_wg_transform_push_data, unix_wg_transform_read_data, unix_wg_transform_get_status, - unix_wg_transform_drain, unix_wg_source_create, unix_wg_source_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 83c939b8453..4711168909b 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1894,7 +1894,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_push_data), X(wg_transform_read_data), X(wg_transform_get_status), - X(wg_transform_drain), X(wg_source_create), X(wg_source_destroy), diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index c8f292e60d5..8bc0507b760 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -85,11 +85,6 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst GST_LOG("transform %p, buffer %p.", transform, buffer); - if (GST_BUFFER_PTS_IS_VALID(buffer)) - transform->segment.start = GST_BUFFER_PTS(buffer); - else if (GST_BUFFER_DURATION_IS_VALID(buffer)) - transform->segment.start += GST_BUFFER_DURATION(buffer); - if (!(sample = gst_sample_new(buffer, transform->output_caps, NULL, NULL))) { GST_ERROR("Failed to allocate transform %p output sample.", transform); @@ -561,50 +556,6 @@ NTSTATUS wg_transform_set_output_format(void *args) return STATUS_SUCCESS; } -NTSTATUS wg_transform_drain(void *args) -{ - struct wg_transform_drain_params *params = args; - struct wg_transform *transform = params->transform; - GstBuffer *input_buffer; - GstFlowReturn ret; - GstEvent *event; - - - while ((input_buffer = gst_atomic_queue_pop(transform->input_queue))) - { - if ((ret = gst_pad_push(transform->my_src, input_buffer))) - { - GST_ERROR("Failed to push transform input, error %d", ret); - return S_OK; - } - } - - if (!gst_pad_peer_query(transform->my_src, transform->drain_query)) - { - GST_ERROR("Drain query failed, transform %p.", transform); - return MF_E_STREAM_ERROR; - } - if (!(event = gst_event_new_segment_done(GST_FORMAT_TIME, transform->segment.start)) - || !gst_pad_push_event(transform->my_src, event)) - { - GST_ERROR("Sending segment done event failed, transform %p.", transform); - return MF_E_STREAM_ERROR; - } - if (!gst_pad_peer_query(transform->my_src, transform->drain_query)) - { - GST_ERROR("Drain query failed, transform %p.", transform); - return MF_E_STREAM_ERROR; - } - if (!(event = gst_event_new_segment(&transform->segment)) - || !gst_pad_push_event(transform->my_src, event)) - { - GST_ERROR("Sending new segment event failed, transform %p.", transform); - return MF_E_STREAM_ERROR; - } - - return S_OK; -} - static void wg_sample_free_notify(void *arg) { struct wg_sample *sample = arg; From 07db7d00146cae673acfce9bc4b22bf2d325deed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 23:10:49 +0200 Subject: [PATCH 413/758] Revert "HACK: winegstreamer: Fake H264 timestamps if framerate cannot be trusted." This reverts commit 0486aee9fe84dbb3ba650747df0f8de7c0c2acb4. CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/h264_decoder.c | 18 +----------------- dlls/winegstreamer/wg_transform.c | 9 ++------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 3088f2f7837..439701098ad 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -58,8 +58,6 @@ struct h264_decoder IMFMediaType *output_type; MFT_OUTPUT_STREAM_INFO output_info; - UINT64 last_pts; - struct wg_format wg_format; struct wg_transform *wg_transform; struct wg_sample_queue *wg_sample_queue; @@ -75,7 +73,6 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) struct wg_format input_format; struct wg_format output_format; - decoder->last_pts = 0; if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); decoder->wg_transform = NULL; @@ -602,10 +599,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct h264_decoder *decoder = impl_from_IMFTransform(iface); - UINT64 frame_rate, duration; struct wg_format wg_format; UINT32 sample_size; - LONGLONG time; + UINT64 frame_rate; GUID subtype; HRESULT hr; @@ -629,20 +625,8 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, sample_size, &wg_format, &samples->dwStatus))) - { wg_sample_queue_flush(decoder->wg_sample_queue, false); - if (FAILED(IMFSample_GetSampleTime(samples->pSample, &time)) - || FAILED(IMFSample_GetSampleDuration(samples->pSample, &time))) - { - frame_rate = (UINT64)decoder->wg_format.u.video.fps_n << 32 | decoder->wg_format.u.video.fps_d; - duration = (UINT64)10000000 * (UINT32)frame_rate / (frame_rate >> 32); - IMFSample_SetSampleTime(samples->pSample, decoder->last_pts); - IMFSample_SetSampleDuration(samples->pSample, duration); - decoder->last_pts += duration; - } - } - if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { decoder->wg_format = wg_format; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 8bc0507b760..7a0e4876dc4 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -61,7 +61,6 @@ struct wg_transform GstSample *output_sample; bool output_caps_changed; GstCaps *output_caps; - bool broken_timestamps; }; static void align_video_info_planes(gsize plane_align, GstVideoInfo *info, GstVideoAlignment *align) @@ -338,10 +337,9 @@ NTSTATUS wg_transform_create(void *args) */ transform->input_max_length = 16; transform->output_plane_align = 15; - if ((element = create_element("h264parse", "base")) - && !append_element(transform->container, element, &first, &last)) + if (!(element = create_element("h264parse", "base")) + || !append_element(transform->container, element, &first, &last)) goto out; - transform->broken_timestamps = !element; /* fallthrough */ case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: @@ -874,9 +872,6 @@ NTSTATUS wg_transform_read_data(void *args) transform->output_sample = NULL; } - if (transform->broken_timestamps) - sample->flags &= ~(WG_SAMPLE_FLAG_HAS_PTS|WG_SAMPLE_FLAG_HAS_DURATION); - params->result = S_OK; wg_allocator_release_sample(transform->allocator, sample, discard_data); return STATUS_SUCCESS; From 415dd099351497f1be0fb1241864bce288e37d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 17 May 2023 14:23:37 +0200 Subject: [PATCH 414/758] winegstreamer: Forbid vaapidecodebin when looking for a specific element. This will eventually fallback to vaapih264dec and similar decoders if VA-API plugins are indeed available. (cherry picked from commit 0c760ef70de7be47684de82fcd5dc7e71aeb3e22) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/unixlib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 77f8dda0928..346adec81f8 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -106,6 +106,16 @@ GstElement *find_element(GstElementFactoryListType type, GstCaps *src_caps, GstC for (tmp = transforms; tmp != NULL && element == NULL; tmp = tmp->next) { name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(tmp->data)); + + if (!strcmp(name, "vaapidecodebin")) + { + /* vaapidecodebin adds asynchronicity which breaks wg_transform synchronous drain / flush + * requirements. Ignore it and use VA-API decoders directly instead. + */ + GST_WARNING("Ignoring vaapidecodebin decoder."); + continue; + } + if (!(element = gst_element_factory_create(GST_ELEMENT_FACTORY(tmp->data), NULL))) GST_WARNING("Failed to create %s element.", name); } From 0a034742c2bd84fa73755b3514a8feb4c55dcd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 May 2023 09:38:10 +0200 Subject: [PATCH 415/758] winegstreamer: Only warn on wg_transform input buffer push errors. Some GStreamer plugins such as openh264 return spurious errors when running the tests. They pass the tests successfull if we simply ignore them. It does not make much difference with returning the error, as they are not supposed to happen anyway, and most of the time the MFT clients don't expect or handle errors. (cherry picked from commit 2a2ed45566eb29c6bef7a610d52f25f65542a9a2) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/wg_transform.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 7a0e4876dc4..27f61ddc9df 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -750,30 +750,25 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsi static bool get_transform_output(struct wg_transform *transform, struct wg_sample *sample) { - GstFlowReturn ret = GST_FLOW_OK; GstBuffer *input_buffer; + GstFlowReturn ret; /* Provide the sample for transform_request_sample to pick it up */ InterlockedIncrement(&sample->refcount); InterlockedExchangePointer((void **)&transform->output_wg_sample, sample); - while (!(transform->output_sample = gst_atomic_queue_pop(transform->output_queue))) + while (!(transform->output_sample = gst_atomic_queue_pop(transform->output_queue)) + && (input_buffer = gst_atomic_queue_pop(transform->input_queue))) { - if (!(input_buffer = gst_atomic_queue_pop(transform->input_queue))) - break; - if ((ret = gst_pad_push(transform->my_src, input_buffer))) - { - GST_ERROR("Failed to push transform input, error %d", ret); - break; - } + GST_WARNING("Failed to push transform input, error %d", ret); } /* Remove the sample so transform_request_sample cannot use it */ if (InterlockedExchangePointer((void **)&transform->output_wg_sample, NULL)) InterlockedDecrement(&sample->refcount); - return ret == GST_FLOW_OK; + return !!transform->output_sample; } NTSTATUS wg_transform_read_data(void *args) @@ -788,12 +783,6 @@ NTSTATUS wg_transform_read_data(void *args) NTSTATUS status; if (!transform->output_sample && !get_transform_output(transform, sample)) - { - wg_allocator_release_sample(transform->allocator, sample, false); - return STATUS_UNSUCCESSFUL; - } - - if (!transform->output_sample) { sample->size = 0; params->result = MF_E_TRANSFORM_NEED_MORE_INPUT; From 7317f257eb76a0ae63a4c3459621f9266c723e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 15:21:18 +0200 Subject: [PATCH 416/758] winegstreamer: Set the default H264 caps profile to "baseline". Some elements such as openh264dec require it to be present. (cherry picked from commit 34b990fe176672a46a63864b6e121ba88ffecb7e) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/wg_format.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 9507d6475b0..5ab2665f38d 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -529,11 +529,10 @@ static GstCaps *wg_format_to_caps_video_h264(const struct wg_format *format) GST_FIXME("H264 profile attribute %u not implemented.", format->u.video_h264.profile); /* fallthrough */ case eAVEncH264VProfile_unknown: - profile = NULL; + profile = "baseline"; break; } - if (profile) - gst_caps_set_simple(caps, "profile", G_TYPE_STRING, profile, NULL); + gst_caps_set_simple(caps, "profile", G_TYPE_STRING, profile, NULL); switch (format->u.video_h264.level) { From c25bbe24cb38fa9edf60c172ba2c95e290a1d9ab Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 31 Mar 2023 19:16:59 -0600 Subject: [PATCH 417/758] winegstreamer: Process MFT_MESSAGE_SET_D3D_MANAGER in h264 decoder. (cherry picked from commit 3f2ff939a10bc9ec14172d2798ea6f02e73048d2) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/h264_decoder.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 439701098ad..211e5faea88 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -61,6 +61,8 @@ struct h264_decoder struct wg_format wg_format; struct wg_transform *wg_transform; struct wg_sample_queue *wg_sample_queue; + + IMFVideoSampleAllocatorEx *allocator; }; static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -240,6 +242,7 @@ static ULONG WINAPI transform_Release(IMFTransform *iface) if (!refcount) { + IMFVideoSampleAllocatorEx_Release(decoder->allocator); if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); if (decoder->input_type) @@ -250,7 +253,6 @@ static ULONG WINAPI transform_Release(IMFTransform *iface) IMFAttributes_Release(decoder->output_attributes); if (decoder->attributes) IMFAttributes_Release(decoder->attributes); - wg_sample_queue_destroy(decoder->wg_sample_queue); free(decoder); } @@ -579,7 +581,14 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %Ix stub!\n", iface, message, param); + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); + + if (message == MFT_MESSAGE_SET_D3D_MANAGER) + return IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param); + + FIXME("Ignoring message %#x.\n", message); return S_OK; } @@ -731,12 +740,16 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) goto failed; if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) goto failed; + if (FAILED(hr = MFCreateVideoSampleAllocatorEx(&IID_IMFVideoSampleAllocatorEx, (void **)&decoder->allocator))) + goto failed; *ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret); return S_OK; failed: + if (decoder->wg_sample_queue) + wg_sample_queue_destroy(decoder->wg_sample_queue); if (decoder->output_attributes) IMFAttributes_Release(decoder->output_attributes); if (decoder->attributes) From 6ff74b8fb210f99a9e421f7e2c9a8fb077ecb06a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 31 Mar 2023 20:08:19 -0600 Subject: [PATCH 418/758] winegstreamer: Provide samples if DXGI device manager is set in h264 decoder. (cherry picked from commit a208589b2732f852d741b14b920714fba390c952) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/h264_decoder.c | 63 +++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 211e5faea88..a04f49abd0e 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -63,6 +63,7 @@ struct h264_decoder struct wg_sample_queue *wg_sample_queue; IMFVideoSampleAllocatorEx *allocator; + BOOL allocator_initialized; }; static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -203,6 +204,26 @@ static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType return S_OK; } +static HRESULT init_allocator(struct h264_decoder *decoder) +{ + HRESULT hr; + + if (decoder->allocator_initialized) + return S_OK; + + if (FAILED(hr = IMFVideoSampleAllocatorEx_InitializeSampleAllocatorEx(decoder->allocator, 10, 10, + decoder->attributes, decoder->output_type))) + return hr; + decoder->allocator_initialized = TRUE; + return S_OK; +} + +static void uninit_allocator(struct h264_decoder *decoder) +{ + IMFVideoSampleAllocatorEx_UninitializeSampleAllocator(decoder->allocator); + decoder->allocator_initialized = FALSE; +} + static HRESULT WINAPI transform_QueryInterface(IMFTransform *iface, REFIID iid, void **out) { struct h264_decoder *decoder = impl_from_IMFTransform(iface); @@ -582,11 +603,22 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { struct h264_decoder *decoder = impl_from_IMFTransform(iface); + HRESULT hr; TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); if (message == MFT_MESSAGE_SET_D3D_MANAGER) - return IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param); + { + if (FAILED(hr = IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param))) + return hr; + + uninit_allocator(decoder); + if (param) + decoder->output_info.dwFlags |= MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + else + decoder->output_info.dwFlags &= ~MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + return S_OK; + } FIXME("Ignoring message %#x.\n", message); return S_OK; @@ -610,6 +642,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, struct h264_decoder *decoder = impl_from_IMFTransform(iface); struct wg_format wg_format; UINT32 sample_size; + IMFSample *sample; UINT64 frame_rate; GUID subtype; HRESULT hr; @@ -623,7 +656,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return MF_E_TRANSFORM_TYPE_NOT_SET; *status = samples->dwStatus = 0; - if (!samples->pSample) + if (!(sample = samples->pSample) && !(decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) return E_INVALIDARG; if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) @@ -632,7 +665,21 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, decoder->wg_format.u.video.height, &sample_size))) return hr; - if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, samples->pSample, + if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) + { + if (FAILED(hr = init_allocator(decoder))) + { + ERR("Failed to initialize allocator, hr %#lx.\n", hr); + return hr; + } + if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, &sample))) + { + ERR("Failed to allocate sample, hr %#lx.\n", hr); + return hr; + } + } + + if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, sample_size, &wg_format, &samples->dwStatus))) wg_sample_queue_flush(decoder->wg_sample_queue, false); @@ -651,6 +698,16 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; + + uninit_allocator(decoder); + } + + if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) + { + if (hr == S_OK) + samples->pSample = sample; + else + IMFSample_Release(sample); } return hr; From 153f88abcc1e4573f8d360b65e297d244467bd9f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 4 Apr 2023 18:14:48 -0600 Subject: [PATCH 419/758] winegstreamer: Pass temporary sample to wg_transform_read_mf() in h264 decoder. (cherry picked from commit 17c86f42de494620efb133ab471bb1b0b8ef9d0d) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/Makefile.in | 2 +- dlls/winegstreamer/h264_decoder.c | 69 +++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index d811cfd810c..7624948dba8 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -2,7 +2,7 @@ MODULE = winegstreamer.dll UNIXLIB = winegstreamer.so IMPORTLIB = winegstreamer IMPORTS = strmbase ole32 oleaut32 msdmo -DELAYIMPORTS = mfplat +DELAYIMPORTS = mfplat mf UNIX_CFLAGS = $(GSTREAMER_CFLAGS) UNIX_LIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index a04f49abd0e..c8b72eb9c26 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -64,6 +64,8 @@ struct h264_decoder IMFVideoSampleAllocatorEx *allocator; BOOL allocator_initialized; + IMFTransform *copier; + IMFMediaBuffer *temp_buffer; }; static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -211,6 +213,11 @@ static HRESULT init_allocator(struct h264_decoder *decoder) if (decoder->allocator_initialized) return S_OK; + if (FAILED(hr = IMFTransform_SetInputType(decoder->copier, 0, decoder->output_type, 0))) + return hr; + if (FAILED(hr = IMFTransform_SetOutputType(decoder->copier, 0, decoder->output_type, 0))) + return hr; + if (FAILED(hr = IMFVideoSampleAllocatorEx_InitializeSampleAllocatorEx(decoder->allocator, 10, 10, decoder->attributes, decoder->output_type))) return hr; @@ -263,7 +270,10 @@ static ULONG WINAPI transform_Release(IMFTransform *iface) if (!refcount) { + IMFTransform_Release(decoder->copier); IMFVideoSampleAllocatorEx_Release(decoder->allocator); + if (decoder->temp_buffer) + IMFMediaBuffer_Release(decoder->temp_buffer); if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); if (decoder->input_type) @@ -636,6 +646,36 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS return wg_transform_push_mf(decoder->wg_transform, sample, decoder->wg_sample_queue); } +static HRESULT output_sample(struct h264_decoder *decoder, IMFSample **out, IMFSample *src_sample) +{ + MFT_OUTPUT_DATA_BUFFER output[1]; + IMFSample *sample; + DWORD status; + HRESULT hr; + + if (FAILED(hr = init_allocator(decoder))) + { + ERR("Failed to initialize allocator, hr %#lx.\n", hr); + return hr; + } + if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, &sample))) + return hr; + + if (FAILED(hr = IMFTransform_ProcessInput(decoder->copier, 0, src_sample, 0))) + { + IMFSample_Release(sample); + return hr; + } + output[0].pSample = sample; + if (FAILED(hr = IMFTransform_ProcessOutput(decoder->copier, 0, 1, output, &status))) + { + IMFSample_Release(sample); + return hr; + } + *out = sample; + return S_OK; +} + static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { @@ -645,6 +685,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, IMFSample *sample; UINT64 frame_rate; GUID subtype; + DWORD size; HRESULT hr; TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); @@ -667,14 +708,21 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { - if (FAILED(hr = init_allocator(decoder))) + if (decoder->temp_buffer) { - ERR("Failed to initialize allocator, hr %#lx.\n", hr); - return hr; + if (FAILED(IMFMediaBuffer_GetMaxLength(decoder->temp_buffer, &size)) || size < sample_size) + { + IMFMediaBuffer_Release(decoder->temp_buffer); + decoder->temp_buffer = NULL; + } } - if (FAILED(hr = IMFVideoSampleAllocatorEx_AllocateSample(decoder->allocator, &sample))) + if (!decoder->temp_buffer && FAILED(hr = MFCreateMemoryBuffer(sample_size, &decoder->temp_buffer))) + return hr; + if (FAILED(hr = MFCreateSample(&sample))) + return hr; + if (FAILED(hr = IMFSample_AddBuffer(sample, decoder->temp_buffer))) { - ERR("Failed to allocate sample, hr %#lx.\n", hr); + IMFSample_Release(sample); return hr; } } @@ -704,10 +752,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { - if (hr == S_OK) - samples->pSample = sample; - else - IMFSample_Release(sample); + if (hr == S_OK && FAILED(hr = output_sample(decoder, &samples->pSample, sample))) + ERR("Failed to output sample, hr %#lx.\n", hr); + IMFSample_Release(sample); } return hr; @@ -799,12 +846,16 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) goto failed; if (FAILED(hr = MFCreateVideoSampleAllocatorEx(&IID_IMFVideoSampleAllocatorEx, (void **)&decoder->allocator))) goto failed; + if (FAILED(hr = MFCreateSampleCopierMFT(&decoder->copier))) + goto failed; *ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret); return S_OK; failed: + if (decoder->allocator) + IMFVideoSampleAllocatorEx_Release(decoder->allocator); if (decoder->wg_sample_queue) wg_sample_queue_destroy(decoder->wg_sample_queue); if (decoder->output_attributes) From 301bf7d0c03edd79a45515a3777f8c9eb6ace326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 13:39:33 +0200 Subject: [PATCH 420/758] winegstreamer: Use an IMFMediaType for the internal stream type. (cherry picked from commit 8caaca177e7b3545296a099a0dd43a375cb13cdf) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/h264_decoder.c | 92 ++++++++++++++++--------------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index c8b72eb9c26..8eeeb52fd60 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -57,8 +57,8 @@ struct h264_decoder MFT_INPUT_STREAM_INFO input_info; IMFMediaType *output_type; MFT_OUTPUT_STREAM_INFO output_info; + IMFMediaType *stream_type; - struct wg_format wg_format; struct wg_transform *wg_transform; struct wg_sample_queue *wg_sample_queue; @@ -107,8 +107,8 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType *media_type) { IMFMediaType *default_type = decoder->output_type; - struct wg_format *wg_format = &decoder->wg_format; UINT32 value, width, height; + MFVideoArea aperture; UINT64 ratio; GUID subtype; HRESULT hr; @@ -118,7 +118,8 @@ static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType if (FAILED(hr = IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &ratio))) { - ratio = (UINT64)wg_format->u.video.width << 32 | wg_format->u.video.height; + if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &ratio))) + ratio = (UINT64)1920 << 32 | 1080; if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ratio))) return hr; } @@ -127,14 +128,16 @@ static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_FRAME_RATE, NULL))) { - ratio = (UINT64)wg_format->u.video.fps_n << 32 | wg_format->u.video.fps_d; + if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_RATE, &ratio))) + ratio = (UINT64)30000 << 32 | 1001; if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ratio))) return hr; } if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_PIXEL_ASPECT_RATIO, NULL))) { - ratio = (UINT64)1 << 32 | 1; /* FIXME: read it from format */ + if (FAILED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_PIXEL_ASPECT_RATIO, &ratio))) + ratio = (UINT64)1 << 32 | 1; if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_PIXEL_ASPECT_RATIO, ratio))) return hr; } @@ -188,16 +191,9 @@ static HRESULT fill_output_media_type(struct h264_decoder *decoder, IMFMediaType } if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, NULL)) - && !IsRectEmpty(&wg_format->u.video.padding)) + && SUCCEEDED(hr = IMFMediaType_GetBlob(decoder->stream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + (BYTE *)&aperture, sizeof(aperture), &value))) { - MFVideoArea aperture = - { - .OffsetX = {.value = wg_format->u.video.padding.left}, - .OffsetY = {.value = wg_format->u.video.padding.top}, - .Area.cx = wg_format->u.video.width - wg_format->u.video.padding.right - wg_format->u.video.padding.left, - .Area.cy = wg_format->u.video.height - wg_format->u.video.padding.bottom - wg_format->u.video.padding.top, - }; - if (FAILED(hr = IMFMediaType_SetBlob(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&aperture, sizeof(aperture)))) return hr; @@ -481,10 +477,9 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) { - decoder->wg_format.u.video.width = frame_size >> 32; - decoder->wg_format.u.video.height = (UINT32)frame_size; - decoder->output_info.cbSize = decoder->wg_format.u.video.width - * decoder->wg_format.u.video.height * 2; + if (FAILED(hr = IMFMediaType_SetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, frame_size))) + WARN("Failed to update stream type frame size, hr %#lx\n", hr); + decoder->output_info.cbSize = (frame_size >> 32) * (UINT32)frame_size * 2; } return S_OK; @@ -493,8 +488,8 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct h264_decoder *decoder = impl_from_IMFTransform(iface); + UINT64 frame_size, stream_frame_size; GUID major, subtype; - UINT64 frame_size; HRESULT hr; ULONG i; @@ -516,9 +511,10 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF if (i == ARRAY_SIZE(h264_decoder_output_types)) return MF_E_INVALIDMEDIATYPE; - if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)) - || (frame_size >> 32) != decoder->wg_format.u.video.width - || (UINT32)frame_size != decoder->wg_format.u.video.height) + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + return MF_E_INVALIDMEDIATYPE; + if (SUCCEEDED(IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &stream_frame_size)) + && frame_size != stream_frame_size) return MF_E_INVALIDMEDIATYPE; if (flags & MFT_SET_TYPE_TEST_ONLY) return S_OK; @@ -676,6 +672,28 @@ static HRESULT output_sample(struct h264_decoder *decoder, IMFSample **out, IMFS return S_OK; } +static HRESULT handle_stream_type_change(struct h264_decoder *decoder, const struct wg_format *format) +{ + UINT64 frame_size, frame_rate; + HRESULT hr; + + if (decoder->stream_type) + IMFMediaType_Release(decoder->stream_type); + if (!(decoder->stream_type = mf_media_type_from_wg_format(format))) + return E_OUTOFMEMORY; + + if (SUCCEEDED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_RATE, &frame_rate)) + && FAILED(hr = IMFMediaType_SetUINT64(decoder->stream_type, &MF_MT_FRAME_RATE, frame_rate))) + WARN("Failed to update stream type frame size, hr %#lx\n", hr); + + if (FAILED(hr = IMFMediaType_GetUINT64(decoder->stream_type, &MF_MT_FRAME_SIZE, &frame_size))) + return hr; + decoder->output_info.cbSize = (frame_size >> 32) * (UINT32)frame_size * 2; + uninit_allocator(decoder); + + return MF_E_TRANSFORM_STREAM_CHANGE; +} + static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { @@ -683,7 +701,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, struct wg_format wg_format; UINT32 sample_size; IMFSample *sample; - UINT64 frame_rate; + UINT64 frame_size; GUID subtype; DWORD size; HRESULT hr; @@ -702,8 +720,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) return hr; - if (FAILED(hr = MFCalculateImageSize(&subtype, decoder->wg_format.u.video.width, - decoder->wg_format.u.video.height, &sample_size))) + if (FAILED(hr = IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size))) + return hr; + if (FAILED(hr = MFCalculateImageSize(&subtype, frame_size >> 32, (UINT32)frame_size, &sample_size))) return hr; if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) @@ -733,21 +752,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { - decoder->wg_format = wg_format; - decoder->output_info.cbSize = ALIGN_SIZE(decoder->wg_format.u.video.width, 0xf) - * ALIGN_SIZE(decoder->wg_format.u.video.height, 0xf) * 2; - - /* keep the frame rate that was requested, GStreamer doesn't provide any */ - if (SUCCEEDED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_RATE, &frame_rate))) - { - decoder->wg_format.u.video.fps_n = frame_rate >> 32; - decoder->wg_format.u.video.fps_d = (UINT32)frame_rate; - } - samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; - - uninit_allocator(decoder); + hr = handle_stream_type_change(decoder, &wg_format); } if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) @@ -821,11 +828,6 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) decoder->IMFTransform_iface.lpVtbl = &transform_vtbl; decoder->refcount = 1; - decoder->wg_format.u.video.format = WG_VIDEO_FORMAT_UNKNOWN; - decoder->wg_format.u.video.width = 1920; - decoder->wg_format.u.video.height = 1080; - decoder->wg_format.u.video.fps_n = 30000; - decoder->wg_format.u.video.fps_d = 1001; decoder->input_info.dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; @@ -834,6 +836,8 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; decoder->output_info.cbSize = 1920 * 1088 * 2; + if (FAILED(hr = MFCreateMediaType(&decoder->stream_type))) + goto failed; if (FAILED(hr = MFCreateAttributes(&decoder->attributes, 16))) goto failed; if (FAILED(hr = IMFAttributes_SetUINT32(decoder->attributes, &MF_LOW_LATENCY, 0))) @@ -862,6 +866,8 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) IMFAttributes_Release(decoder->output_attributes); if (decoder->attributes) IMFAttributes_Release(decoder->attributes); + if (decoder->stream_type) + IMFMediaType_Release(decoder->stream_type); free(decoder); return hr; } From f09a8b2f4ddc9c84e2b6bf37ffaba984975ed2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 14:42:28 +0200 Subject: [PATCH 421/758] winegstreamer: Generate H264 timestamps using the input type frame rate. And remove h264parse element requirement. (cherry picked from commit 52387aa1a4e25ee2dec4117201658b32b160a4ca) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/h264_decoder.c | 16 +++++++++++++++- dlls/winegstreamer/wg_transform.c | 3 --- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 8eeeb52fd60..97289f69a4d 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -53,6 +53,7 @@ struct h264_decoder IMFAttributes *attributes; IMFAttributes *output_attributes; + UINT64 sample_time; IMFMediaType *input_type; MFT_INPUT_STREAM_INFO input_info; IMFMediaType *output_type; @@ -700,8 +701,9 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, struct h264_decoder *decoder = impl_from_IMFTransform(iface); struct wg_format wg_format; UINT32 sample_size; + LONGLONG duration; IMFSample *sample; - UINT64 frame_size; + UINT64 frame_size, frame_rate; GUID subtype; DWORD size; HRESULT hr; @@ -748,8 +750,20 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, sample, sample_size, &wg_format, &samples->dwStatus))) + { wg_sample_queue_flush(decoder->wg_sample_queue, false); + if (FAILED(IMFMediaType_GetUINT64(decoder->input_type, &MF_MT_FRAME_RATE, &frame_rate))) + frame_rate = (UINT64)30000 << 32 | 1001; + + duration = (UINT64)10000000 * (UINT32)frame_rate / (frame_rate >> 32); + if (FAILED(IMFSample_SetSampleTime(sample, decoder->sample_time))) + WARN("Failed to set sample time\n"); + if (FAILED(IMFSample_SetSampleDuration(sample, duration))) + WARN("Failed to set sample duration\n"); + decoder->sample_time += duration; + } + if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 27f61ddc9df..a8ee302469c 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -337,9 +337,6 @@ NTSTATUS wg_transform_create(void *args) */ transform->input_max_length = 16; transform->output_plane_align = 15; - if (!(element = create_element("h264parse", "base")) - || !append_element(transform->container, element, &first, &last)) - goto out; /* fallthrough */ case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: From 637695cd7afdade7e696b748c7f7c5c87f2c169d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 15:03:22 +0200 Subject: [PATCH 422/758] winegstreamer: Use the output wg_format in CAPS sink query. Instead of constraining the output caps to the current resolution, which breaks when streams with different resolutions are concatenated. (cherry picked from commit 4d1a331c6619703e5766d50ddf0399eaa079ffe3) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/wg_transform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index a8ee302469c..b64944264ee 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -56,6 +56,7 @@ struct wg_transform GstElement *video_flip; guint output_plane_align; + struct wg_format output_format; struct wg_sample *output_wg_sample; GstAtomicQueue *output_queue; GstSample *output_sample; @@ -174,7 +175,8 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery gchar *str; gst_query_parse_caps(query, &filter); - caps = gst_caps_ref(transform->output_caps); + if (!(caps = wg_format_to_caps(&transform->output_format))) + break; if (filter) { @@ -303,6 +305,7 @@ NTSTATUS wg_transform_create(void *args) goto out; transform->input_max_length = 1; transform->output_plane_align = 0; + transform->output_format = output_format; if (!(src_caps = wg_format_to_caps(&input_format))) goto out; @@ -503,6 +506,7 @@ NTSTATUS wg_transform_set_output_format(void *args) GST_ERROR("Failed to convert format %p to caps.", format); return STATUS_UNSUCCESSFUL; } + transform->output_format = *format; if (gst_caps_is_always_compatible(transform->output_caps, caps)) { From dde7dee7d11168bd1ec9e1fad36cd3abe02c4742 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 28 Feb 2023 12:07:55 -0600 Subject: [PATCH 423/758] winegstreamer: Don't pre-check sample size in wg_transform_read_mf(). (cherry picked from commit 596dfad38b9cb327b73c6b2f1c42e42080218cb6) CW-Bug-Id: #21804 CW-Bug-Id: #21813 CW-Bug-Id: #22299 --- dlls/winegstreamer/wg_sample.c | 5 ---- dlls/winegstreamer/wg_transform.c | 38 +++++++++++++++++-------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/dlls/winegstreamer/wg_sample.c b/dlls/winegstreamer/wg_sample.c index d20583bdb22..1278ccf7347 100644 --- a/dlls/winegstreamer/wg_sample.c +++ b/dlls/winegstreamer/wg_sample.c @@ -353,11 +353,6 @@ HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample, return hr; wg_sample->size = 0; - if (wg_sample->max_size < sample_size) - { - wg_sample_release(wg_sample); - return MF_E_BUFFERTOOSMALL; - } if (FAILED(hr = wg_transform_read_data(transform, wg_sample, format))) { diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index b64944264ee..564a5e12083 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -604,19 +604,19 @@ NTSTATUS wg_transform_push_data(void *args) return STATUS_SUCCESS; } -static bool copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_align, +static NTSTATUS copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_align, struct wg_sample *sample, gsize *total_size) { + NTSTATUS status = STATUS_UNSUCCESSFUL; GstVideoFrame src_frame, dst_frame; GstVideoInfo src_info, dst_info; GstVideoAlignment align; GstBuffer *dst_buffer; - bool ret = false; if (!gst_video_info_from_caps(&src_info, caps)) { GST_ERROR("Failed to get video info from caps."); - return false; + return STATUS_UNSUCCESSFUL; } dst_info = src_info; @@ -625,14 +625,14 @@ static bool copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_alig if (sample->max_size < dst_info.size) { GST_ERROR("Output buffer is too small."); - return false; + return STATUS_BUFFER_TOO_SMALL; } if (!(dst_buffer = gst_buffer_new_wrapped_full(0, sample->data, sample->max_size, 0, sample->max_size, 0, NULL))) { GST_ERROR("Failed to wrap wg_sample into GstBuffer"); - return false; + return STATUS_UNSUCCESSFUL; } gst_buffer_set_size(dst_buffer, dst_info.size); *total_size = sample->size = dst_info.size; @@ -645,7 +645,9 @@ static bool copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_alig GST_ERROR("Failed to map destination frame."); else { - if (!(ret = gst_video_frame_copy(&dst_frame, &src_frame))) + if (gst_video_frame_copy(&dst_frame, &src_frame)) + status = STATUS_SUCCESS; + else GST_ERROR("Failed to copy video frame."); gst_video_frame_unmap(&dst_frame); } @@ -653,16 +655,16 @@ static bool copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_alig } gst_buffer_unref(dst_buffer); - return ret; + return status; } -static bool copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample *sample, +static NTSTATUS copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample *sample, gsize *total_size) { GstMapInfo info; if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) - return false; + return STATUS_UNSUCCESSFUL; if (sample->max_size >= info.size) sample->size = info.size; @@ -679,14 +681,15 @@ static bool copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample *samp gst_buffer_resize(buffer, sample->size, -1); *total_size = info.size; - return true; + return STATUS_SUCCESS; } static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsize plane_align, struct wg_sample *sample) { - bool ret, needs_copy; gsize total_size; + bool needs_copy; + NTSTATUS status; GstMapInfo info; if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) @@ -696,20 +699,21 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsi return STATUS_UNSUCCESSFUL; } needs_copy = info.data != sample->data; + total_size = sample->size = info.size; gst_buffer_unmap(buffer, &info); - if ((ret = !needs_copy)) - total_size = sample->size = info.size; + if (!needs_copy) + status = STATUS_SUCCESS; else if (stream_type_from_caps(caps) == GST_STREAM_TYPE_VIDEO) - ret = copy_video_buffer(buffer, caps, plane_align, sample, &total_size); + status = copy_video_buffer(buffer, caps, plane_align, sample, &total_size); else - ret = copy_buffer(buffer, caps, sample, &total_size); + status = copy_buffer(buffer, caps, sample, &total_size); - if (!ret) + if (status) { GST_ERROR("Failed to copy buffer %p", buffer); sample->size = 0; - return STATUS_UNSUCCESSFUL; + return status; } if (GST_BUFFER_PTS_IS_VALID(buffer)) From aa3d9f595d8ef35eac478419442266ed2836217f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 14:37:04 +0200 Subject: [PATCH 424/758] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for H264 decoder. (cherry picked from commit 16347299c132a128f4b72a95c1e4207f469c8ce6) CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/h264_decoder.c | 13 ++++++++---- dlls/winegstreamer/main.c | 15 +++++++++++++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_transform.c | 35 +++++++++++++++++++++++++++++++ 7 files changed, 63 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e2c32d12b64..81aefe4234f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -105,6 +105,7 @@ struct wg_transform *wg_transform_create(const struct wg_format *input_format, void wg_transform_destroy(struct wg_transform *transform); bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_format *format); bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); +HRESULT wg_transform_drain(struct wg_transform *transform); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 97289f69a4d..96888e49178 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -614,8 +614,9 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ TRACE("iface %p, message %#x, param %Ix.\n", iface, message, param); - if (message == MFT_MESSAGE_SET_D3D_MANAGER) + switch (message) { + case MFT_MESSAGE_SET_D3D_MANAGER: if (FAILED(hr = IMFVideoSampleAllocatorEx_SetDirectXManager(decoder->allocator, (IUnknown *)param))) return hr; @@ -625,10 +626,14 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ else decoder->output_info.dwFlags &= ~MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; return S_OK; - } - FIXME("Ignoring message %#x.\n", message); - return S_OK; + case MFT_MESSAGE_COMMAND_DRAIN: + return wg_transform_drain(decoder->wg_transform); + + default: + FIXME("Ignoring message %#x.\n", message); + return S_OK; + } } static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 5cbaf49e38a..00469b3d0b6 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -415,6 +415,21 @@ bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_fo return !WINE_UNIX_CALL(unix_wg_transform_set_output_format, ¶ms); } +HRESULT wg_transform_drain(struct wg_transform *transform) +{ + NTSTATUS status; + + TRACE("transform %p.\n", transform); + + if ((status = WINE_UNIX_CALL(unix_wg_transform_drain, transform))) + { + WARN("wg_transform_drain returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + return S_OK; +} + struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]) { diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 025ef83ed99..1f4ebf0a2df 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -66,6 +66,7 @@ extern NTSTATUS wg_transform_set_output_format(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_push_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_read_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_get_status(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; /* wg_source.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 660a32374e2..072b5d045da 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -409,6 +409,7 @@ enum unix_funcs unix_wg_transform_push_data, unix_wg_transform_read_data, unix_wg_transform_get_status, + unix_wg_transform_drain, unix_wg_source_create, unix_wg_source_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 4711168909b..83c939b8453 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1894,6 +1894,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_push_data), X(wg_transform_read_data), X(wg_transform_get_status), + X(wg_transform_drain), X(wg_source_create), X(wg_source_destroy), diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 564a5e12083..7de2fb5f5a5 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -879,3 +879,38 @@ NTSTATUS wg_transform_get_status(void *args) params->accepts_input = gst_atomic_queue_length(transform->input_queue) < transform->input_max_length; return STATUS_SUCCESS; } + +NTSTATUS wg_transform_drain(void *args) +{ + struct wg_transform *transform = args; + GstBuffer *input_buffer; + GstFlowReturn ret; + GstEvent *event; + + GST_LOG("transform %p", transform); + + while ((input_buffer = gst_atomic_queue_pop(transform->input_queue))) + { + if ((ret = gst_pad_push(transform->my_src, input_buffer))) + GST_WARNING("Failed to push transform input, error %d", ret); + } + + if (!(event = gst_event_new_segment_done(GST_FORMAT_TIME, -1)) + || !gst_pad_push_event(transform->my_src, event)) + goto error; + if (!(event = gst_event_new_eos()) + || !gst_pad_push_event(transform->my_src, event)) + goto error; + if (!(event = gst_event_new_stream_start("stream")) + || !gst_pad_push_event(transform->my_src, event)) + goto error; + if (!(event = gst_event_new_segment(&transform->segment)) + || !gst_pad_push_event(transform->my_src, event)) + goto error; + + return STATUS_SUCCESS; + +error: + GST_ERROR("Failed to drain transform %p.", transform); + return STATUS_UNSUCCESSFUL; +} From dd6d4375c5bc6fc61d9802feec890bff9db7d030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 15:07:20 +0200 Subject: [PATCH 425/758] winegstreamer: Implement MFT_MESSAGE_COMMAND_FLUSH for H264 decoder. (cherry picked from commit 4f4ee0e16b343c2c6f935c465c59dc3c705ebd65) CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/h264_decoder.c | 3 +++ dlls/winegstreamer/main.c | 15 +++++++++++++++ dlls/winegstreamer/unix_private.h | 1 + dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_parser.c | 1 + dlls/winegstreamer/wg_transform.c | 24 ++++++++++++++++++++++++ 7 files changed, 46 insertions(+) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 81aefe4234f..b1bc3a26cb2 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -106,6 +106,7 @@ void wg_transform_destroy(struct wg_transform *transform); bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_format *format); bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); HRESULT wg_transform_drain(struct wg_transform *transform); +HRESULT wg_transform_flush(struct wg_transform *transform); struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 96888e49178..f8d4ea10d5e 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -630,6 +630,9 @@ static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_ case MFT_MESSAGE_COMMAND_DRAIN: return wg_transform_drain(decoder->wg_transform); + case MFT_MESSAGE_COMMAND_FLUSH: + return wg_transform_flush(decoder->wg_transform); + default: FIXME("Ignoring message %#x.\n", message); return S_OK; diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 00469b3d0b6..47fc4c8c6e1 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -430,6 +430,21 @@ HRESULT wg_transform_drain(struct wg_transform *transform) return S_OK; } +HRESULT wg_transform_flush(struct wg_transform *transform) +{ + NTSTATUS status; + + TRACE("transform %p.\n", transform); + + if ((status = WINE_UNIX_CALL(unix_wg_transform_flush, transform))) + { + WARN("wg_transform_flush returned status %#lx\n", status); + return HRESULT_FROM_NT(status); + } + + return S_OK; +} + struct wg_source *wg_source_create(const WCHAR *url, uint64_t file_size, const void *data, uint32_t size, WCHAR mime_type[256]) { diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 1f4ebf0a2df..434b81715e8 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -67,6 +67,7 @@ extern NTSTATUS wg_transform_push_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_read_data(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_get_status(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_drain(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_transform_flush(void *args) DECLSPEC_HIDDEN; /* wg_source.c */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 072b5d045da..1825d90ee11 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -410,6 +410,7 @@ enum unix_funcs unix_wg_transform_read_data, unix_wg_transform_get_status, unix_wg_transform_drain, + unix_wg_transform_flush, unix_wg_source_create, unix_wg_source_destroy, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 83c939b8453..84405ef52d8 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1895,6 +1895,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_transform_read_data), X(wg_transform_get_status), X(wg_transform_drain), + X(wg_transform_flush), X(wg_source_create), X(wg_source_destroy), diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 7de2fb5f5a5..30c3b6e59d3 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -914,3 +914,27 @@ NTSTATUS wg_transform_drain(void *args) GST_ERROR("Failed to drain transform %p.", transform); return STATUS_UNSUCCESSFUL; } + +NTSTATUS wg_transform_flush(void *args) +{ + struct wg_transform *transform = args; + GstBuffer *input_buffer; + GstSample *sample; + NTSTATUS status; + + GST_LOG("transform %p", transform); + + while ((input_buffer = gst_atomic_queue_pop(transform->input_queue))) + gst_buffer_unref(input_buffer); + + if ((status = wg_transform_drain(transform))) + return status; + + while ((sample = gst_atomic_queue_pop(transform->output_queue))) + gst_sample_unref(sample); + if ((sample = transform->output_sample)) + gst_sample_unref(sample); + transform->output_sample = NULL; + + return STATUS_SUCCESS; +} From f478dd815e8c38407d22ce81f26d666c670cb99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 17 May 2023 14:23:15 +0200 Subject: [PATCH 426/758] winegstreamer: Pass desired output plane alignment to wg_transform_create. (cherry picked from commit f83922fd98d79a6d79c538755c2889d6e362e1a3) CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/aac_decoder.c | 6 ++++-- dlls/winegstreamer/color_convert.c | 6 ++++-- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/h264_decoder.c | 9 +++++++-- dlls/winegstreamer/main.c | 3 ++- dlls/winegstreamer/quartz_transform.c | 9 ++++++--- dlls/winegstreamer/resampler.c | 6 ++++-- dlls/winegstreamer/unixlib.h | 6 ++++++ dlls/winegstreamer/video_decoder.c | 3 ++- dlls/winegstreamer/video_processor.c | 6 ++++-- dlls/winegstreamer/wg_transform.c | 12 ++++++------ dlls/winegstreamer/wma_decoder.c | 14 ++++++++++---- 12 files changed, 56 insertions(+), 26 deletions(-) diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index d79ede69c9d..1fc545fc283 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -67,6 +67,7 @@ static struct aac_decoder *impl_from_IMFTransform(IMFTransform *iface) static HRESULT try_create_wg_transform(struct aac_decoder *decoder) { struct wg_format input_format, output_format; + struct wg_transform_attrs attrs = {0}; if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); @@ -80,7 +81,7 @@ static HRESULT try_create_wg_transform(struct aac_decoder *decoder) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; return S_OK; @@ -625,13 +626,14 @@ HRESULT aac_decoder_create(REFIID riid, void **ret) }, }; static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_AUDIO_MPEG4}; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct aac_decoder *decoder; HRESULT hr; TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); - if (!(transform = wg_transform_create(&input_format, &output_format))) + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR_(winediag)("GStreamer doesn't support WMA decoding, please install appropriate plugins\n"); return E_FAIL; diff --git a/dlls/winegstreamer/color_convert.c b/dlls/winegstreamer/color_convert.c index ed591c8e669..624191daf33 100644 --- a/dlls/winegstreamer/color_convert.c +++ b/dlls/winegstreamer/color_convert.c @@ -98,6 +98,7 @@ static inline struct color_convert *impl_from_IUnknown(IUnknown *iface) static HRESULT try_create_wg_transform(struct color_convert *impl) { struct wg_format input_format, output_format; + struct wg_transform_attrs attrs = {0}; if (impl->wg_transform) wg_transform_destroy(impl->wg_transform); @@ -111,7 +112,7 @@ static HRESULT try_create_wg_transform(struct color_convert *impl) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; return S_OK; @@ -936,13 +937,14 @@ HRESULT color_convert_create(IUnknown *outer, IUnknown **out) .height = 1080, }, }; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct color_convert *impl; HRESULT hr; TRACE("outer %p, out %p.\n", outer, out); - if (!(transform = wg_transform_create(&input_format, &output_format))) + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR_(winediag)("GStreamer doesn't support video conversion, please install appropriate plugins.\n"); return E_FAIL; diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index b1bc3a26cb2..c53483c0ca7 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -101,7 +101,7 @@ void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags); struct wg_transform *wg_transform_create(const struct wg_format *input_format, - const struct wg_format *output_format); + const struct wg_format *output_format, const struct wg_transform_attrs *attrs); void wg_transform_destroy(struct wg_transform *transform); bool wg_transform_set_output_format(struct wg_transform *transform, struct wg_format *format); bool wg_transform_get_status(struct wg_transform *transform, bool *accepts_input); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index f8d4ea10d5e..81b06445df9 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -76,6 +76,10 @@ static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) static HRESULT try_create_wg_transform(struct h264_decoder *decoder) { + struct wg_transform_attrs attrs = + { + .output_plane_align = 15, + }; struct wg_format input_format; struct wg_format output_format; @@ -99,7 +103,7 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) output_format.u.video.fps_d = 0; output_format.u.video.fps_n = 0; - if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; return S_OK; @@ -832,13 +836,14 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) }, }; static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_VIDEO_H264}; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct h264_decoder *decoder; HRESULT hr; TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); - if (!(transform = wg_transform_create(&input_format, &output_format))) + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR_(winediag)("GStreamer doesn't support H.264 decoding, please install appropriate plugins\n"); return E_FAIL; diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 47fc4c8c6e1..261d810795f 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -326,12 +326,13 @@ void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, } struct wg_transform *wg_transform_create(const struct wg_format *input_format, - const struct wg_format *output_format) + const struct wg_format *output_format, const struct wg_transform_attrs *attrs) { struct wg_transform_create_params params = { .input_format = input_format, .output_format = output_format, + .attrs = attrs, }; TRACE("input_format %p, output_format %p.\n", input_format, output_format); diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 09ad4862410..84f6bbd6361 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -98,6 +98,7 @@ static HRESULT transform_init_stream(struct strmbase_filter *iface) { struct transform *filter = impl_from_strmbase_filter(iface); struct wg_format input_format, output_format; + struct wg_transform_attrs attrs = {0}; HRESULT hr; if (filter->source.pin.peer) @@ -111,7 +112,7 @@ static HRESULT transform_init_stream(struct strmbase_filter *iface) if (FAILED(hr = wg_sample_queue_create(&filter->sample_queue))) return hr; - filter->transform = wg_transform_create(&input_format, &output_format); + filter->transform = wg_transform_create(&input_format, &output_format, &attrs); if (!filter->transform) { wg_sample_queue_destroy(filter->sample_queue); @@ -710,11 +711,12 @@ HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out) .rate = 44100, }, }; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct transform *object; HRESULT hr; - transform = wg_transform_create(&input_format, &output_format); + transform = wg_transform_create(&input_format, &output_format, &attrs); if (!transform) { ERR_(winediag)("GStreamer doesn't support MPEG-1 audio decoding, please install appropriate plugins.\n"); @@ -844,11 +846,12 @@ HRESULT mpeg_layer3_decoder_create(IUnknown *outer, IUnknown **out) .rate = 44100, }, }; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct transform *object; HRESULT hr; - transform = wg_transform_create(&input_format, &output_format); + transform = wg_transform_create(&input_format, &output_format, &attrs); if (!transform) { ERR_(winediag)("GStreamer doesn't support MPEG-1 audio decoding, please install appropriate plugins.\n"); diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 88e9727ff21..7ed8cf48fbf 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -56,6 +56,7 @@ struct resampler static HRESULT try_create_wg_transform(struct resampler *impl) { struct wg_format input_format, output_format; + struct wg_transform_attrs attrs = {0}; if (impl->wg_transform) wg_transform_destroy(impl->wg_transform); @@ -69,7 +70,7 @@ static HRESULT try_create_wg_transform(struct resampler *impl) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; return S_OK; @@ -895,13 +896,14 @@ HRESULT resampler_create(IUnknown *outer, IUnknown **out) .rate = 44100, }, }; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct resampler *impl; HRESULT hr; TRACE("outer %p, out %p.\n", outer, out); - if (!(transform = wg_transform_create(&input_format, &output_format))) + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR_(winediag)("GStreamer doesn't support audio resampling, please install appropriate plugins.\n"); return E_FAIL; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 1825d90ee11..750f47c1820 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -298,11 +298,17 @@ struct wg_parser_stream_seek_params DWORD start_flags, stop_flags; }; +struct wg_transform_attrs +{ + UINT32 output_plane_align; +}; + struct wg_transform_create_params { struct wg_transform *transform; const struct wg_format *input_format; const struct wg_format *output_format; + const struct wg_transform_attrs *attrs; }; struct wg_transform_push_data_params diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 04d175c7910..abcadf90a32 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -68,6 +68,7 @@ static struct video_decoder *impl_from_IMFTransform(IMFTransform *iface) static HRESULT try_create_wg_transform(struct video_decoder *decoder) { + struct wg_transform_attrs attrs = {0}; struct wg_format input_format; struct wg_format output_format; @@ -86,7 +87,7 @@ static HRESULT try_create_wg_transform(struct video_decoder *decoder) output_format.u.video.fps_d = 0; output_format.u.video.fps_n = 0; - if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR("Failed to create transform with input major_type %u.\n", input_format.major_type); return E_FAIL; diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 21bc46b0bb3..3cb0ccc090b 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -88,6 +88,7 @@ struct video_processor static HRESULT try_create_wg_transform(struct video_processor *impl) { struct wg_format input_format, output_format; + struct wg_transform_attrs attrs = {0}; if (impl->wg_transform) wg_transform_destroy(impl->wg_transform); @@ -101,7 +102,7 @@ static HRESULT try_create_wg_transform(struct video_processor *impl) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; return S_OK; @@ -602,13 +603,14 @@ HRESULT video_processor_create(REFIID riid, void **ret) .height = 1080, }, }; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct video_processor *impl; HRESULT hr; TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); - if (!(transform = wg_transform_create(&input_format, &output_format))) + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR_(winediag)("GStreamer doesn't support video conversion, please install appropriate plugins.\n"); return E_FAIL; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 30c3b6e59d3..dc5af0fae02 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -43,6 +43,8 @@ struct wg_transform { + struct wg_transform_attrs attrs; + GstElement *container; GstAllocator *allocator; GstPad *my_src, *my_sink; @@ -55,7 +57,6 @@ struct wg_transform bool input_is_flipped; GstElement *video_flip; - guint output_plane_align; struct wg_format output_format; struct wg_sample *output_wg_sample; GstAtomicQueue *output_queue; @@ -111,7 +112,7 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery { case GST_QUERY_ALLOCATION: { - gsize plane_align = transform->output_plane_align; + gsize plane_align = transform->attrs.output_plane_align; GstStructure *config, *params; GstVideoAlignment align; gboolean needs_pool; @@ -303,8 +304,8 @@ NTSTATUS wg_transform_create(void *args) goto out; if (!(transform->allocator = wg_allocator_create(transform_request_sample, transform))) goto out; + transform->attrs = *params->attrs; transform->input_max_length = 1; - transform->output_plane_align = 0; transform->output_format = output_format; if (!(src_caps = wg_format_to_caps(&input_format))) @@ -339,7 +340,6 @@ NTSTATUS wg_transform_create(void *args) * to match its expectations. */ transform->input_max_length = 16; - transform->output_plane_align = 15; /* fallthrough */ case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: @@ -805,7 +805,7 @@ NTSTATUS wg_transform_read_data(void *args) if (format) { - gsize plane_align = transform->output_plane_align; + gsize plane_align = transform->attrs.output_plane_align; GstVideoAlignment align; GstVideoInfo info; @@ -837,7 +837,7 @@ NTSTATUS wg_transform_read_data(void *args) } if ((status = read_transform_output_data(output_buffer, output_caps, - transform->output_plane_align, sample))) + transform->attrs.output_plane_align, sample))) { wg_allocator_release_sample(transform->allocator, sample, false); return status; diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 10a41a0c92f..10bcd4162a1 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -76,6 +76,7 @@ static inline struct wma_decoder *impl_from_IUnknown(IUnknown *iface) static HRESULT try_create_wg_transform(struct wma_decoder *decoder) { struct wg_format input_format, output_format; + struct wg_transform_attrs attrs = {0}; if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); @@ -89,7 +90,7 @@ static HRESULT try_create_wg_transform(struct wma_decoder *decoder) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE; - if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format))) + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; return S_OK; @@ -741,6 +742,8 @@ static HRESULT WINAPI media_object_SetInputType(IMediaObject *iface, DWORD index return VFW_E_INVALIDMEDIATYPE; if (!(flags & DMO_SET_TYPEF_TEST_ONLY)) { + struct wg_transform_attrs attrs = {0}; + impl->input_format = wg_format; if (!impl->output_format.major_type) return S_OK; @@ -749,7 +752,7 @@ static HRESULT WINAPI media_object_SetInputType(IMediaObject *iface, DWORD index wg_transform_destroy(impl->wg_transform); impl->wg_transform = NULL; - if (!(impl->wg_transform = wg_transform_create(&impl->input_format, &impl->output_format))) + if (!(impl->wg_transform = wg_transform_create(&impl->input_format, &impl->output_format, &attrs))) return E_FAIL; } @@ -777,6 +780,8 @@ static HRESULT WINAPI media_object_SetOutputType(IMediaObject *iface, DWORD inde return VFW_E_INVALIDMEDIATYPE; if (!(flags & DMO_SET_TYPEF_TEST_ONLY)) { + struct wg_transform_attrs attrs = {0}; + impl->output_format = wg_format; if (!impl->input_format.major_type) return S_OK; @@ -785,7 +790,7 @@ static HRESULT WINAPI media_object_SetOutputType(IMediaObject *iface, DWORD inde wg_transform_destroy(impl->wg_transform); impl->wg_transform = NULL; - if (!(impl->wg_transform = wg_transform_create(&impl->input_format, &impl->output_format))) + if (!(impl->wg_transform = wg_transform_create(&impl->input_format, &impl->output_format, &attrs))) return E_FAIL; } @@ -1007,13 +1012,14 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out) }, }; static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_AUDIO_WMA}; + struct wg_transform_attrs attrs = {0}; struct wg_transform *transform; struct wma_decoder *decoder; HRESULT hr; TRACE("outer %p, out %p.\n", outer, out); - if (!(transform = wg_transform_create(&input_format, &output_format))) + if (!(transform = wg_transform_create(&input_format, &output_format, &attrs))) { ERR_(winediag)("GStreamer doesn't support WMA decoding, please install appropriate plugins\n"); return E_FAIL; From 3c3c16ded3352026da63965ddc5fb64f03cb1948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 May 2023 10:07:56 +0200 Subject: [PATCH 427/758] winegstreamer: Pass desired input queue length to wg_transform_create. (cherry picked from commit 036166faf7671d2e2c4c11422a55170323503cff) CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/color_convert.c | 2 +- dlls/winegstreamer/h264_decoder.c | 6 ++++++ dlls/winegstreamer/resampler.c | 2 +- dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/video_processor.c | 2 +- dlls/winegstreamer/wg_transform.c | 14 ++------------ 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/dlls/winegstreamer/color_convert.c b/dlls/winegstreamer/color_convert.c index 624191daf33..598b2aa5b43 100644 --- a/dlls/winegstreamer/color_convert.c +++ b/dlls/winegstreamer/color_convert.c @@ -98,7 +98,7 @@ static inline struct color_convert *impl_from_IUnknown(IUnknown *iface) static HRESULT try_create_wg_transform(struct color_convert *impl) { struct wg_format input_format, output_format; - struct wg_transform_attrs attrs = {0}; + struct wg_transform_attrs attrs = {.input_queue_length = 15}; if (impl->wg_transform) wg_transform_destroy(impl->wg_transform); diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 81b06445df9..8795d12169f 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -76,9 +76,15 @@ static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) static HRESULT try_create_wg_transform(struct h264_decoder *decoder) { + /* Call of Duty: Black Ops 3 doesn't care about the ProcessInput/ProcessOutput + * return values, it calls them in a specific order and expects the decoder + * transform to be able to queue its input buffers. We need to use a buffer list + * to match its expectations. + */ struct wg_transform_attrs attrs = { .output_plane_align = 15, + .input_queue_length = 15, }; struct wg_format input_format; struct wg_format output_format; diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 7ed8cf48fbf..9c87a5431b2 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -56,7 +56,7 @@ struct resampler static HRESULT try_create_wg_transform(struct resampler *impl) { struct wg_format input_format, output_format; - struct wg_transform_attrs attrs = {0}; + struct wg_transform_attrs attrs = {.input_queue_length = 15}; if (impl->wg_transform) wg_transform_destroy(impl->wg_transform); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 750f47c1820..82f063e2da7 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -301,6 +301,7 @@ struct wg_parser_stream_seek_params struct wg_transform_attrs { UINT32 output_plane_align; + UINT32 input_queue_length; }; struct wg_transform_create_params diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 3cb0ccc090b..0164557234a 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -88,7 +88,7 @@ struct video_processor static HRESULT try_create_wg_transform(struct video_processor *impl) { struct wg_format input_format, output_format; - struct wg_transform_attrs attrs = {0}; + struct wg_transform_attrs attrs = {.input_queue_length = 15}; if (impl->wg_transform) wg_transform_destroy(impl->wg_transform); diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index dc5af0fae02..db9af28488a 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -51,7 +51,6 @@ struct wg_transform GstSegment segment; GstQuery *drain_query; - guint input_max_length; GstAtomicQueue *input_queue; bool input_is_flipped; @@ -305,7 +304,6 @@ NTSTATUS wg_transform_create(void *args) if (!(transform->allocator = wg_allocator_create(transform_request_sample, transform))) goto out; transform->attrs = *params->attrs; - transform->input_max_length = 1; transform->output_format = output_format; if (!(src_caps = wg_format_to_caps(&input_format))) @@ -334,13 +332,6 @@ NTSTATUS wg_transform_create(void *args) switch (input_format.major_type) { case WG_MAJOR_TYPE_VIDEO_H264: - /* Call of Duty: Black Ops 3 doesn't care about the ProcessInput/ProcessOutput - * return values, it calls them in a specific order and expects the decoder - * transform to be able to queue its input buffers. We need to use a buffer list - * to match its expectations. - */ - transform->input_max_length = 16; - /* fallthrough */ case WG_MAJOR_TYPE_AUDIO_MPEG1: case WG_MAJOR_TYPE_AUDIO_MPEG4: case WG_MAJOR_TYPE_AUDIO_WMA: @@ -357,7 +348,6 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_AUDIO: case WG_MAJOR_TYPE_VIDEO: - transform->input_max_length = 16; break; case WG_MAJOR_TYPE_UNKNOWN: GST_FIXME("Format %u not implemented!", input_format.major_type); @@ -571,7 +561,7 @@ NTSTATUS wg_transform_push_data(void *args) guint length; length = gst_atomic_queue_length(transform->input_queue); - if (length >= transform->input_max_length) + if (length >= transform->attrs.input_queue_length + 1) { GST_INFO("Refusing %u bytes, %u buffers already queued", sample->size, length); params->result = MF_E_NOTACCEPTING; @@ -876,7 +866,7 @@ NTSTATUS wg_transform_get_status(void *args) struct wg_transform_get_status_params *params = args; struct wg_transform *transform = params->transform; - params->accepts_input = gst_atomic_queue_length(transform->input_queue) < transform->input_max_length; + params->accepts_input = gst_atomic_queue_length(transform->input_queue) < transform->attrs.input_queue_length + 1; return STATUS_SUCCESS; } From 83d7bc0496eb7794d55bc9772dcbf3d1bb80b0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 25 May 2023 14:15:43 +0200 Subject: [PATCH 428/758] winegstreamer: Implement MF_LOW_LATENCY attribute and latency query. (cherry picked from commit 226f8b7c28408762bc54c32406d57377b19f0866) CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/h264_decoder.c | 4 ++++ dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_transform.c | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 8795d12169f..c6e817df6b0 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -88,6 +88,7 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) }; struct wg_format input_format; struct wg_format output_format; + UINT32 low_latency; if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); @@ -109,6 +110,9 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) output_format.u.video.fps_d = 0; output_format.u.video.fps_n = 0; + if (SUCCEEDED(IMFAttributes_GetUINT32(decoder->attributes, &MF_LOW_LATENCY, &low_latency))) + attrs.low_latency = !!low_latency; + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 82f063e2da7..e3d259a7fd2 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -302,6 +302,7 @@ struct wg_transform_attrs { UINT32 output_plane_align; UINT32 input_queue_length; + BOOL low_latency; }; struct wg_transform_create_params diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index db9af28488a..1369b5f6591 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -101,6 +101,26 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst return GST_FLOW_OK; } +static gboolean transform_src_query_latency(struct wg_transform *transform, GstQuery *query) +{ + GST_LOG("transform %p, query %p", transform, query); + gst_query_set_latency(query, transform->attrs.low_latency, 0, 0); + return true; +} + +static gboolean transform_src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_transform *transform = gst_pad_get_element_private(pad); + + switch (query->type) + { + case GST_QUERY_LATENCY: + return transform_src_query_latency(transform, query); + default: + return gst_pad_query_default(pad, parent, query); + } +} + static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_transform *transform = gst_pad_get_element_private(pad); @@ -311,6 +331,9 @@ NTSTATUS wg_transform_create(void *args) if (!(transform->my_src = create_pad_with_caps(GST_PAD_SRC, src_caps))) goto out; + gst_pad_set_element_private(transform->my_src, transform); + gst_pad_set_query_function(transform->my_src, transform_src_query_cb); + if (!(transform->output_caps = wg_format_to_caps(&output_format))) goto out; if (!(transform->my_sink = create_pad_with_caps(GST_PAD_SINK, transform->output_caps))) From 83d591848918a59256c7f41c17a3665631ee739c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:18:25 -0600 Subject: [PATCH 429/758] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for aac decoder. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/mf/tests/transform.c | 9 +++++++-- dlls/winegstreamer/aac_decoder.c | 13 ++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index d3dca90cdf7..36181c33d20 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -2470,8 +2470,13 @@ static void test_aac_decoder_subtype(const struct attribute_desc *input_type_des hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "ProcessMessage returned %#lx\n", hr); - hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); - ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); + if (0) + { + /* This is fine on Windows but currently MFT_MESSAGE_COMMAND_DRAIN removes input sample from the queue + * and makes next _ProcessInput succeed on Wine breaking the tests below. */ + hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); + ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); + } hr = MFCreateCollection(&output_samples); ok(hr == S_OK, "MFCreateCollection returned %#lx\n", hr); diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 1fc545fc283..69c91b0d6d0 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -535,7 +535,18 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(decoder->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From b888da27145e09121a14f1e34516d40578530253 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:47:47 -0600 Subject: [PATCH 430/758] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for resampler. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/resampler.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/resampler.c b/dlls/winegstreamer/resampler.c index 9c87a5431b2..9df0d6fe563 100644 --- a/dlls/winegstreamer/resampler.c +++ b/dlls/winegstreamer/resampler.c @@ -506,7 +506,18 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + struct resampler *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!impl->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(impl->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From 154ae99860e150affaa0bed805eade3166b98b90 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:48:48 -0600 Subject: [PATCH 431/758] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for video processor. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/video_processor.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 0164557234a..4c5e822d14a 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -506,7 +506,18 @@ static HRESULT WINAPI video_processor_ProcessEvent(IMFTransform *iface, DWORD id static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %#Ix stub!\n", iface, message, param); + struct video_processor *impl = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!impl->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(impl->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From 99bcdc13c6894b1719caacd2468f45cccdad4c4d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:50:38 -0600 Subject: [PATCH 432/758] winegstreamer: Implement MFT_MESSAGE_COMMAND_DRAIN for wma decoder. CW-Bug-Id: #21804 CW-Bug-Id: #22299 --- dlls/winegstreamer/wma_decoder.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 10bcd4162a1..eff8c414ea8 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -523,7 +523,18 @@ static HRESULT WINAPI transform_ProcessEvent(IMFTransform *iface, DWORD id, IMFM static HRESULT WINAPI transform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { - FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + struct wma_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, message %#x, param %p.\n", iface, message, (void *)param); + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (message == MFT_MESSAGE_COMMAND_DRAIN) + return wg_transform_drain(decoder->wg_transform); + + FIXME("Ignoring message %#x.\n", message); + return S_OK; } From 3045469eb06d1106caf08e6e599ab662e08fea3e Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 2 Jun 2023 17:30:13 +0800 Subject: [PATCH 433/758] winegstreamer: Fix check for non-zero padding in mf_media_type_from_wg_format_video(). Similar to 35f9091, IsRectEmpty() is the wrong way to check if padding is empty. CW-Bug-Id: #22299 --- dlls/winegstreamer/mfplat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 9e0f3db23bb..09b0cc9eb2a 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -538,7 +538,8 @@ static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format * stride = -stride; IMFMediaType_SetUINT32(type, &MF_MT_DEFAULT_STRIDE, stride); - if (!IsRectEmpty(&format->u.video.padding)) + if (format->u.video.padding.left || format->u.video.padding.right + || format->u.video.padding.top || format->u.video.padding.bottom) { MFVideoArea aperture = { From f3c50b0f2395b1be3759292933e38859abe9a7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 29 Jun 2023 11:48:55 +0200 Subject: [PATCH 434/758] winegstreamer: Allow concurrent wait_on_sample in the media source. Fixes deadlock in Disaster Report 4: Summer Memories. CW-Bug-Id: #22377 --- dlls/winegstreamer/media_source_old.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/media_source_old.c b/dlls/winegstreamer/media_source_old.c index 624ecc86cad..d60bed89eb4 100644 --- a/dlls/winegstreamer/media_source_old.c +++ b/dlls/winegstreamer/media_source_old.c @@ -47,6 +47,9 @@ struct media_stream DWORD stream_id; BOOL active; BOOL eos; + + DWORD busy; + CONDITION_VARIABLE cond; }; enum source_async_op @@ -532,13 +535,21 @@ static void wait_on_sample(struct media_stream *stream, IUnknown *token) struct media_source *source = impl_from_IMFMediaSource(stream->media_source); PROPVARIANT empty_var = {.vt = VT_EMPTY}; struct wg_parser_buffer buffer; + BOOL ret; TRACE("%p, %p\n", stream, token); - if (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) - { + stream->busy = TRUE; + LeaveCriticalSection(&source->cs); + ret = wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer); + EnterCriticalSection(&source->cs); + stream->busy = FALSE; + WakeConditionVariable(&stream->cond); + + if (source->state == SOURCE_SHUTDOWN) + WARN("media source has been shutdown, returning\n"); + else if (ret) send_buffer(stream, &buffer, token); - } else { stream->eos = TRUE; @@ -1398,6 +1409,7 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *source = impl_from_IMFMediaSource(iface); + UINT i; TRACE("%p.\n", iface); @@ -1411,6 +1423,14 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) source->state = SOURCE_SHUTDOWN; + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + wg_parser_stream_disable(stream->wg_stream); + while (stream->busy) + SleepConditionVariableCS(&stream->cond, &source->cs, INFINITE); + } + wg_parser_disconnect(source->wg_parser); source->read_thread_shutdown = true; From a893bfd7de1808bff0f7324dd7599a25960dfa83 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 17 May 2023 11:19:46 -0500 Subject: [PATCH 435/758] mscoree: Update Wine Mono to 8.0.0. (cherry picked from commit 0a3fe99d2bb00ad2f8867551915a82c7983e6f5a) --- dlls/appwiz.cpl/addons.c | 4 ++-- dlls/mscoree/mscoree_private.h | 2 +- tools/gitlab/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index 762c0b31f73..a53e818bbbb 100644 --- a/dlls/appwiz.cpl/addons.c +++ b/dlls/appwiz.cpl/addons.c @@ -58,10 +58,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl); #define GECKO_SHA "???" #endif -#define MONO_VERSION "7.4.1" +#define MONO_VERSION "8.0.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "4721de007ecd0019cc18e144a882c290da3314d7e1bc77f57c404675e644b9fe" +#define MONO_SHA "75b3f45dca1dc89857fe9e932da78710f64cc6d49ef1ab0c723a177085b4711b" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index 64a5efe8d10..a06333b6874 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -45,7 +45,7 @@ extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version) extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count) DECLSPEC_HIDDEN; extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func) DECLSPEC_HIDDEN; -#define WINE_MONO_VERSION "7.4.1" +#define WINE_MONO_VERSION "8.0.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index da34390bc1f..04ee21eb9e7 100644 --- a/tools/gitlab/test.yml +++ b/tools/gitlab/test.yml @@ -7,7 +7,7 @@ variables: GIT_STRATEGY: none GECKO_VER: 2.47.3 - MONO_VER: 7.4.0 + MONO_VER: 8.0.0 cache: - key: wine-gecko-$GECKO_VER paths: From 98584a48a908e53fd242896e26f430dae29765a6 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 8 May 2023 14:09:39 -0500 Subject: [PATCH 436/758] mscoree: Use updated preload hook function. (cherry picked from commit ddc9ef10c9de1ff8885db312f013a069dee21933) --- dlls/mscoree/metahost.c | 30 ++++++++++++++++++++++++------ dlls/mscoree/mscoree_private.h | 7 ++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c index 23dd34ed6e3..22fed41cf15 100644 --- a/dlls/mscoree/metahost.c +++ b/dlls/mscoree/metahost.c @@ -131,11 +131,13 @@ void (CDECL *mono_thread_manage)(void); void (CDECL *mono_trace_set_print_handler)(MonoPrintCallback callback); void (CDECL *mono_trace_set_printerr_handler)(MonoPrintCallback callback); static void (CDECL *wine_mono_install_assembly_preload_hook)(WineMonoAssemblyPreLoadFunc func, void *user_data); +static void (CDECL *wine_mono_install_assembly_preload_hook_v2)(WineMonoAssemblyPreLoadFunc func, void *user_data); static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path); static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data); static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, int *search_path, void *user_data); +static MonoAssembly* CDECL wine_mono_assembly_preload_hook_v2_fn(MonoAssemblyName *aname, char **assemblies_path, int *flags, void *user_data); static void CDECL mono_shutdown_callback_fn(MonoProfiler *prof); @@ -252,6 +254,7 @@ static HRESULT load_mono(LPCWSTR mono_path) LOAD_OPT_MONO_FUNCTION(mono_trace_set_print_handler, set_print_handler_dummy); LOAD_OPT_MONO_FUNCTION(mono_trace_set_printerr_handler, set_print_handler_dummy); LOAD_OPT_MONO_FUNCTION(wine_mono_install_assembly_preload_hook, NULL); + LOAD_OPT_MONO_FUNCTION(wine_mono_install_assembly_preload_hook_v2, NULL); #undef LOAD_OPT_MONO_FUNCTION @@ -282,7 +285,9 @@ static HRESULT load_mono(LPCWSTR mono_path) mono_config_parse(NULL); - if (wine_mono_install_assembly_preload_hook) + if (wine_mono_install_assembly_preload_hook_v2) + wine_mono_install_assembly_preload_hook_v2(wine_mono_assembly_preload_hook_v2_fn, NULL); + else if (wine_mono_install_assembly_preload_hook) wine_mono_install_assembly_preload_hook(wine_mono_assembly_preload_hook_fn, NULL); else mono_install_assembly_preload_hook(mono_assembly_preload_hook_fn, NULL); @@ -1745,11 +1750,20 @@ static BOOL compile_assembly(const char *source, const char *target, char *targe static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { - int dummy; - return wine_mono_assembly_preload_hook_fn(aname, assemblies_path, &dummy, user_data); + int flags = 0; + return wine_mono_assembly_preload_hook_v2_fn(aname, assemblies_path, &flags, user_data); } static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName *aname, char **assemblies_path, int *halt_search, void *user_data) +{ + int flags = 0; + MonoAssembly* result = wine_mono_assembly_preload_hook_v2_fn(aname, assemblies_path, &flags, user_data); + if (flags & WINE_PRELOAD_SKIP_PRIVATE_PATH) + *halt_search = 1; + return result; +} + +static MonoAssembly* CDECL wine_mono_assembly_preload_hook_v2_fn(MonoAssemblyName *aname, char **assemblies_path, int *flags, void *user_data) { HRESULT hr; MonoAssembly *result=NULL; @@ -1902,8 +1916,6 @@ static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName * } } - /* FIXME: We should search the given paths before the GAC. */ - if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0) { stringnameW_size = MultiByteToWideChar(CP_UTF8, 0, stringname, -1, NULL, 0); @@ -1934,6 +1946,12 @@ static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName * ERR("Failed to load %s, status=%u\n", debugstr_w(path), stat); HeapFree(GetProcessHeap(), 0, pathA); + + if (result) + { + *flags |= WINE_PRELOAD_SET_GAC; + goto done; + } } } } @@ -1943,7 +1961,7 @@ static MonoAssembly* CDECL wine_mono_assembly_preload_hook_fn(MonoAssemblyName * if ((search_flags & ASSEMBLY_SEARCH_PRIVATEPATH) == 0) { TRACE("skipping AppDomain search path due to override setting\n"); - *halt_search = 1; + *flags |= WINE_PRELOAD_SKIP_PRIVATE_PATH; } done: diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index a06333b6874..50ed656a656 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -143,7 +143,12 @@ typedef enum { typedef MonoAssembly* (CDECL *MonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, void *user_data); -typedef MonoAssembly* (CDECL *WineMonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, int *halt_search, void *user_data); +#define WINE_PRELOAD_CONTINUE 0 +#define WINE_PRELOAD_SKIP_PRIVATE_PATH 1 +#define WINE_PRELOAD_SKIP_GAC 2 +#define WINE_PRELOAD_SET_GAC 4 + +typedef MonoAssembly* (CDECL *WineMonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, int *flags, void *user_data); typedef void (CDECL *MonoProfileFunc)(MonoProfiler *prof); From 2c6488f556e8007eae046f96617e967254242b0b Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Tue, 9 May 2023 13:20:21 -0500 Subject: [PATCH 437/758] mscoree: Search the Mono GAC before the appdomain paths. (cherry picked from commit 629d2d09a0323f8456d78be0cc08d8206ed722df) --- dlls/mscoree/metahost.c | 44 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c index 22fed41cf15..6327a338596 100644 --- a/dlls/mscoree/metahost.c +++ b/dlls/mscoree/metahost.c @@ -130,6 +130,7 @@ MonoThread* (CDECL *mono_thread_attach)(MonoDomain *domain); void (CDECL *mono_thread_manage)(void); void (CDECL *mono_trace_set_print_handler)(MonoPrintCallback callback); void (CDECL *mono_trace_set_printerr_handler)(MonoPrintCallback callback); +static MonoAssembly* (CDECL *wine_mono_assembly_load_from_gac)(MonoAssemblyName *aname, MonoImageOpenStatus *status, int refonly); static void (CDECL *wine_mono_install_assembly_preload_hook)(WineMonoAssemblyPreLoadFunc func, void *user_data); static void (CDECL *wine_mono_install_assembly_preload_hook_v2)(WineMonoAssemblyPreLoadFunc func, void *user_data); @@ -253,6 +254,7 @@ static HRESULT load_mono(LPCWSTR mono_path) LOAD_OPT_MONO_FUNCTION(mono_set_crash_chaining, set_crash_chaining_dummy); LOAD_OPT_MONO_FUNCTION(mono_trace_set_print_handler, set_print_handler_dummy); LOAD_OPT_MONO_FUNCTION(mono_trace_set_printerr_handler, set_print_handler_dummy); + LOAD_OPT_MONO_FUNCTION(wine_mono_assembly_load_from_gac, NULL); LOAD_OPT_MONO_FUNCTION(wine_mono_install_assembly_preload_hook, NULL); LOAD_OPT_MONO_FUNCTION(wine_mono_install_assembly_preload_hook_v2, NULL); @@ -1375,16 +1377,19 @@ HRESULT CLRMetaHostPolicy_CreateInstance(REFIID riid, void **ppobj) * Assembly search override settings: * * WINE_MONO_OVERRIDES=*,Gac=n - * Never search the GAC for libraries. + * Never search the Windows GAC for libraries. + * + * WINE_MONO_OVERRIDES=*,MonoGac=n + * Never search the Mono GAC for libraries. * * WINE_MONO_OVERRIDES=*,PrivatePath=n * Never search the AppDomain search path for libraries. * * WINE_MONO_OVERRIDES=Microsoft.Xna.Framework,Gac=n - * Never search the GAC for Microsoft.Xna.Framework + * Never search the Windows GAC for Microsoft.Xna.Framework * * WINE_MONO_OVERRIDES=Microsoft.Xna.Framework.*,Gac=n;Microsoft.Xna.Framework.GamerServices,Gac=y - * Never search the GAC for Microsoft.Xna.Framework, or any library starting + * Never search the Windows GAC for Microsoft.Xna.Framework, or any library starting * with Microsoft.Xna.Framework, except for Microsoft.Xna.Framework.GamerServices */ @@ -1392,7 +1397,8 @@ HRESULT CLRMetaHostPolicy_CreateInstance(REFIID riid, void **ppobj) #define ASSEMBLY_SEARCH_GAC 1 #define ASSEMBLY_SEARCH_UNDEFINED 2 #define ASSEMBLY_SEARCH_PRIVATEPATH 4 -#define ASSEMBLY_SEARCH_DEFAULT (ASSEMBLY_SEARCH_GAC|ASSEMBLY_SEARCH_PRIVATEPATH) +#define ASSEMBLY_SEARCH_MONOGAC 8 +#define ASSEMBLY_SEARCH_DEFAULT (ASSEMBLY_SEARCH_GAC|ASSEMBLY_SEARCH_PRIVATEPATH|ASSEMBLY_SEARCH_MONOGAC) typedef struct override_entry { char *name; @@ -1441,6 +1447,14 @@ static void parse_override_entry(override_entry *entry, const char *string, int entry->flags &= ~ASSEMBLY_SEARCH_GAC; } break; + case 7: + if (!_strnicmp(string, "monogac", 7)) { + if (IS_OPTION_TRUE(*value)) + entry->flags |= ASSEMBLY_SEARCH_MONOGAC; + else if (IS_OPTION_FALSE(*value)) + entry->flags &= ~ASSEMBLY_SEARCH_MONOGAC; + } + break; case 11: if (!_strnicmp(string, "privatepath", 11)) { if (IS_OPTION_TRUE(*value)) @@ -1568,7 +1582,7 @@ static DWORD get_basename_search_flags(const char *basename, MonoAssemblyName *a if (strcmp(basename, "Microsoft.Xna.Framework.*") == 0 && mono_assembly_name_get_version(aname, NULL, NULL, NULL) == 4) /* Use FNA as a replacement for XNA4. */ - return 0; + return ASSEMBLY_SEARCH_MONOGAC; return ASSEMBLY_SEARCH_UNDEFINED; } @@ -1958,6 +1972,26 @@ static MonoAssembly* CDECL wine_mono_assembly_preload_hook_v2_fn(MonoAssemblyNam else TRACE("skipping Windows GAC search due to override setting\n"); + if (wine_mono_assembly_load_from_gac) + { + if (search_flags & ASSEMBLY_SEARCH_MONOGAC) + { + result = wine_mono_assembly_load_from_gac (aname, &stat, FALSE); + + if (result) + { + TRACE("found in Mono GAC\n"); + *flags |= WINE_PRELOAD_SET_GAC; + goto done; + } + } + else + { + *flags |= WINE_PRELOAD_SKIP_GAC; + TRACE("skipping Mono GAC search due to override setting\n"); + } + } + if ((search_flags & ASSEMBLY_SEARCH_PRIVATEPATH) == 0) { TRACE("skipping AppDomain search path due to override setting\n"); From afa6f487509fc6bd5a485e67a5aa0f2c52f94843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 6 Jun 2023 16:16:10 +0200 Subject: [PATCH 438/758] HACK: winegstreamer: Add MF_MT_VIDEO_NOMINAL_RANGE on every video stream types. This seems to be currently required for the source reader to match media types. We need to confirm that the attribute is supposed to be there. CW-Bug-Id: #22324 --- dlls/winegstreamer/media_source_old.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/media_source_old.c b/dlls/winegstreamer/media_source_old.c index d60bed89eb4..c9a76349e82 100644 --- a/dlls/winegstreamer/media_source_old.c +++ b/dlls/winegstreamer/media_source_old.c @@ -921,8 +921,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) goto done; } - IMFMediaType_SetUINT32(base_type, &MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Normal); - IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype); stream_types[0] = base_type; @@ -955,6 +953,12 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) stream_types[type_count++] = iyuv_type; } } + + for (i = 0; i < type_count; i++) + { + IMFMediaType_SetUINT32(stream_types[i], &MF_MT_VIDEO_NOMINAL_RANGE, + MFNominalRange_Normal); + } } else if (format.major_type == WG_MAJOR_TYPE_AUDIO) { From 593b10acfd739e665dba37ea1937827d1d469663 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 5 Jun 2023 23:49:53 +0100 Subject: [PATCH 439/758] winevulkan: Include function name in asserts. It is really useful to see at a glance what function is segfaulting/crashing here when debugging games/apps. This code is generated, and when generating WineVulkan against different Vulkan spec versions, etc it may be misleading that the Line: referred to by the assert dialog does not match what is generated locally on a different machine for the same Wine version/commit. Signed-off-by: Joshua Ashton (cherry picked from commit fc7e2041134505f9c122a34c2662490f025a574e) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 9ac1f5875f2..fec0aca6623 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -807,7 +807,7 @@ class VkFunction(object): body += " UNIX_CALL({0}, ¶ms);\n".format(self.name) else: body += " status = UNIX_CALL({0}, ¶ms);\n".format(self.name) - body += " assert(!status);\n" + body += " assert(!status && \"{0}\");\n".format(self.name) if self.type != "void": body += " return params.result;\n" From 9f9a97247a9df142e88bb324dcc7376b6281a229 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Thu, 26 Jan 2023 11:15:36 +0100 Subject: [PATCH 440/758] winevulkan: Update to VK spec version 1.3.240. (cherry picked from commit 83199991ed9df2e3869e60d98b7ead78019caaf2) --- dlls/winevulkan/make_vulkan | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index fec0aca6623..9aeeb11d788 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.237" +VK_XML_VERSION = "1.3.240" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -101,6 +101,7 @@ UNSUPPORTED_EXTENSIONS = [ "VK_KHR_external_fence_win32", # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. + "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml "VK_KHR_win32_keyed_mutex", "VK_NV_external_memory_rdma", # Needs shared resources work. From 0ec6f16ac132464c01ff5764c5590ca89dfa34a2 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Mon, 13 Feb 2023 18:34:22 +0100 Subject: [PATCH 441/758] winevulkan: Fix a typo in a comment. (cherry picked from commit 96c8d383603d508468bd574f58a05a66395e4e80) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 9aeeb11d788..7732950ecd9 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -1229,7 +1229,7 @@ class VkVariable(object): parent = self.parent len = prefix - # check if lenght is a member of another struct (for example pAllocateInfo->commandBufferCount) + # check if length is a member of another struct (for example pAllocateInfo->commandBufferCount) i = len_str.find("->") if i != -1: var = parent[parent.index(len_str[0:i])] From e2af7c99e46b8c88668dd82eb37d8ac886f57d9b Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 3 Mar 2023 20:47:43 +0100 Subject: [PATCH 442/758] winevulkan: Deal with per api xml entries. Otherwise we will have duplicate members/params with the new 242 xml. (cherry picked from commit 53c7ccec44e2cac1f3d117b6fc81193e8c8388d2) --- dlls/winevulkan/make_vulkan | 80 ++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 7732950ecd9..fe239fbfa7c 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -345,6 +345,8 @@ class Direction(Enum): INPUT = 1 OUTPUT = 2 +def api_is_vulkan(obj): + return "vulkan" in obj.get("api", "vulkan").split(",") class VkBaseType(object): def __init__(self, name, _type, alias=None, requires=None): @@ -395,6 +397,9 @@ class VkDefine(object): @staticmethod def from_xml(define): + if not api_is_vulkan(define): + return None + name_elem = define.find("name") if name_elem is None: @@ -471,6 +476,9 @@ class VkEnum(object): @staticmethod def from_xml(enum): + if not api_is_vulkan(enum): + return None + name = enum.attrib.get("name") bitwidth = int(enum.attrib.get("bitwidth", "32")) result = VkEnum(name, bitwidth) @@ -634,6 +642,9 @@ class VkFunction(object): Returns: VkFunction """ + if not api_is_vulkan(command): + return None + func_name = command.attrib.get("name") func_type = alias.type params = alias.params @@ -642,6 +653,9 @@ class VkFunction(object): @staticmethod def from_xml(command, types): + if not api_is_vulkan(command): + return None + proto = command.find("proto") func_name = proto.find("name").text func_type = proto.find("type").text @@ -649,7 +663,8 @@ class VkFunction(object): params = [] for param in command.findall("param"): vk_param = VkParam.from_xml(param, types, params) - params.append(vk_param) + if vk_param: + params.append(vk_param) return VkFunction(_type=func_type, name=func_name, params=params) @@ -1005,6 +1020,9 @@ class VkFunctionPointer(object): @staticmethod def from_xml(funcpointer): + if not api_is_vulkan(funcpointer): + return None + members = [] begin = None @@ -1085,6 +1103,9 @@ class VkHandle(object): @staticmethod def from_xml(handle): + if not api_is_vulkan(handle): + return None + name = handle.find("name").text _type = handle.find("type").text parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice. @@ -1434,6 +1455,9 @@ class VkMember(VkVariable): def from_xml(member, returnedonly, parent): """ Helper function for parsing a member tag within a struct or union. """ + if not api_is_vulkan(member): + return None + name_elem = member.find("name") type_elem = member.find("type") @@ -1678,6 +1702,9 @@ class VkParam(VkVariable): def from_xml(param, types, parent): """ Helper function to create VkParam from xml. """ + if not api_is_vulkan(param): + return None + # Parameter parsing is slightly tricky. All the data is contained within # a param tag, but some data is within subtags while others are text # before or after the type tag. @@ -1986,6 +2013,9 @@ class VkStruct(Sequence): @staticmethod def from_xml(struct): + if not api_is_vulkan(struct): + return None + # Unions and structs are the same parsing wise, but we need to # know which one we are dealing with later on for code generation. union = True if struct.attrib["category"] == "union" else False @@ -2014,7 +2044,8 @@ class VkStruct(Sequence): s = VkStruct(name, [], returnedonly, structextends, union=union) for member in struct.findall("member"): vk_member = VkMember.from_xml(member, returnedonly, s) - s.members.append(vk_member) + if vk_member: + s.members.append(vk_member) return s @@ -3426,13 +3457,15 @@ class VkRegistry(object): continue func = VkFunction.from_xml(command, self.types) - funcs[func.name] = func + if func: + funcs[func.name] = func for command in alias_commands: alias_name = command.attrib.get("alias") alias = funcs[alias_name] func = VkFunction.from_alias(command, alias) - funcs[func.name] = func + if func: + funcs[func.name] = func # To make life easy for the code generation, separate all function # calls out in the 4 types of Vulkan functions: @@ -3470,7 +3503,9 @@ class VkRegistry(object): _type = enum.attrib.get("type") if _type in ("enum", "bitmask"): - enums[name] = VkEnum.from_xml(enum) + enum_obj = VkEnum.from_xml(enum) + if enum_obj: + enums[name] = enum_obj else: # If no type is set, we are dealing with API constants. for value in enum.findall("enum"): @@ -3730,8 +3765,11 @@ class VkRegistry(object): if tail is not None: _type += tail.strip() basetype = VkBaseType(name, _type) - base_types.append(basetype) - type_info["data"] = basetype + if basetype: + base_types.append(basetype) + type_info["data"] = basetype + else: + continue # Basic C types don't need us to define them, but we do need data for them if type_info["requires"] == "vk_platform": @@ -3752,8 +3790,11 @@ class VkRegistry(object): if type_info["category"] == "define": define = VkDefine.from_xml(t) - defines.append(define) - type_info["data"] = define + if define: + defines.append(define) + type_info["data"] = define + else: + continue if type_info["category"] == "enum": name = t.attrib.get("name") @@ -3769,13 +3810,19 @@ class VkRegistry(object): if type_info["category"] == "funcpointer": funcpointer = VkFunctionPointer.from_xml(t) - funcpointers.append(funcpointer) - type_info["data"] = funcpointer + if funcpointer: + funcpointers.append(funcpointer) + type_info["data"] = funcpointer + else: + continue if type_info["category"] == "handle": handle = VkHandle.from_xml(t) - handles.append(handle) - type_info["data"] = handle + if handle: + handles.append(handle) + type_info["data"] = handle + else: + continue if type_info["category"] in ["struct", "union"]: # We store unions among structs as some structs depend @@ -3783,8 +3830,11 @@ class VkRegistry(object): # generation anyway. The official Vulkan scripts use # a similar kind of hack. struct = VkStruct.from_xml(t) - structs.append(struct) - type_info["data"] = struct + if struct: + structs.append(struct) + type_info["data"] = struct + else: + continue # Name is in general within a name tag else it is an optional # attribute on the type tag. From f4f4fbbb5593777aafc4988781a61d66a92a5e5b Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 3 Mar 2023 20:53:07 +0100 Subject: [PATCH 443/758] winevulkan: Only parse extensions for Vulkan. 242 adds VulkanSC only extensions (cherry picked from commit e500ca7648438067778d7a33955cfc1da10f7a3d) --- dlls/winevulkan/make_vulkan | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index fe239fbfa7c..33609b7c7e1 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -3613,8 +3613,9 @@ class VkRegistry(object): if cmd_name in self.funcs: self.funcs[cmd_name].extensions.add(ext_name) - # Some extensions are not ready or have numbers reserved as a place holder. - if ext.attrib["supported"] == "disabled": + # Some extensions are not ready or have numbers reserved as a place holder + # or are only supported for VulkanSC. + if not "vulkan" in ext.attrib["supported"].split(","): LOGGER.debug("Skipping disabled extension: {0}".format(ext_name)) skipped_exts.append(ext_name) return From f70eb0b541be7c815a3205ef84608138df697398 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 3 Mar 2023 20:54:40 +0100 Subject: [PATCH 444/758] winevulkan: Skip features that are not part of Vulkan. (cherry picked from commit 8fc724927a739dcd62a298d4f93e59184575cf3c) --- dlls/winevulkan/make_vulkan | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 33609b7c7e1..df8e4e27dd9 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -3707,6 +3707,8 @@ class VkRegistry(object): """ Parse the feature section, which describes Core commands and types needed. """ for feature in root.findall("./feature"): + if not api_is_vulkan(feature): + continue feature_name = feature.attrib["name"] for require in feature.findall("require"): LOGGER.info("Including features for {0}".format(require.attrib.get("comment"))) From a5358da281ebf636b6aaf0e6efdcae5baf4eb20b Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 3 Mar 2023 21:26:31 +0100 Subject: [PATCH 445/758] winevulkan: Add basic support for extension dependencies. (cherry picked from commit 421140b6246a5efd3352a21345f1b7b69b3cc980) --- dlls/winevulkan/make_vulkan | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index df8e4e27dd9..66f71ae8384 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -3656,6 +3656,11 @@ class VkRegistry(object): if len(set(requires).intersection(skipped_exts)) > 0: skipped_exts.append(ext_name) return + elif "depends" in ext.attrib: + # The syntax for this is more complex, but this is good enough for now. + if any([sext in ext.attrib["depends"] for sext in skipped_exts]): + skipped_exts.append(ext_name) + return LOGGER.debug("Loading extension: {0}".format(ext_name)) From 44b4d97006a8e2277f119a9a8de0d94e8a417d53 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 3 Mar 2023 21:34:37 +0100 Subject: [PATCH 446/758] winevulkan: Update to VK spec version 1.3.242. (cherry picked from commit 1eed39fffe86ec60dee0678ba2303fee7ad78aec) --- dlls/winevulkan/make_vulkan | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 66f71ae8384..b3329b2b868 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.240" +VK_XML_VERSION = "1.3.242" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -1783,7 +1783,7 @@ class VkParam(VkVariable): self.format_conv = "wine_dbgstr_longlong({0})" elif self.type == "HANDLE": self.format_str = "%p" - elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t"]: + elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t", "NvSciBufObj", "NvSciBufAttrList", "NvSciSyncAttrList"]: # Don't care about specific types for non-Windows platforms. self.format_str = "" else: From 2b186b792dbb4c7b16a3d793a7a958e1c79fbe41 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 31 Mar 2023 15:50:26 +0200 Subject: [PATCH 447/758] winevulkan: Update to VK spec version 1.3.246. (cherry picked from commit 36f5da51d0f6205ad8f03010e7fc9fe9eaf4e8d2) Modified to accomodate downstream support for VK_KHR_external_semaphore_win32. --- dlls/winevulkan/make_vulkan | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index b3329b2b868..e0b0a7ecffb 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.242" +VK_XML_VERSION = "1.3.246" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -99,6 +99,7 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_full_screen_exclusive", "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32", + "VK_KHR_map_memory2", # Needs wow64 handling and is useless for now. # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml From 4561edd47077e372d73ca0752fd911f5835d6c55 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Fri, 7 Apr 2023 18:25:05 +0200 Subject: [PATCH 448/758] winevulkan: Add support for VK_KHR_map_memory2. (cherry picked from commit 0a56a4ada251cd1071acfbbf64a28ec4afdd28c3) --- dlls/winevulkan/make_vulkan | 3 +- dlls/winevulkan/vulkan.c | 62 ++++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index e0b0a7ecffb..cf67c2c6764 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -99,7 +99,6 @@ UNSUPPORTED_EXTENSIONS = [ "VK_EXT_full_screen_exclusive", "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32", - "VK_KHR_map_memory2", # Needs wow64 handling and is useless for now. # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml @@ -214,7 +213,9 @@ FUNCTION_OVERRIDES = { "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pAllocateInfo"}, "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkMapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, + "vkMapMemory2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkUnmapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, + "vkUnmapMemory2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkCreateBuffer" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkCreateImage" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetSemaphoreCounterValue" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 260f196b81b..c42f1472e16 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2882,21 +2882,46 @@ void wine_vkFreeMemory(VkDevice handle, VkDeviceMemory memory_handle, const VkAl free(memory); } -VkResult wine_vkMapMemory(VkDevice handle, VkDeviceMemory memory_handle, VkDeviceSize offset, +VkResult wine_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **data) +{ + const VkMemoryMapInfoKHR info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_INFO_KHR, + .flags = flags, + .memory = memory, + .offset = offset, + .size = size, + }; + + return wine_vkMapMemory2KHR(device, &info, data); +} + +VkResult wine_vkMapMemory2KHR(VkDevice handle, const VkMemoryMapInfoKHR *map_info, void **data) { struct wine_device *device = wine_device_from_handle(handle); - struct wine_device_memory *memory = wine_device_memory_from_handle(memory_handle); + struct wine_device_memory *memory = wine_device_memory_from_handle(map_info->memory); + VkMemoryMapInfoKHR info = *map_info; VkResult result; + info.memory = memory->memory; if (memory->mapping) { - *data = (char *)memory->mapping + offset; + *data = (char *)memory->mapping + info.offset; TRACE("returning %p\n", *data); return VK_SUCCESS; } - result = device->funcs.p_vkMapMemory(device->device, memory->memory, offset, size, flags, data); + if (device->funcs.p_vkMapMemory2KHR) + { + result = device->funcs.p_vkMapMemory2KHR(device->device, &info, data); + } + else + { + assert(!info.pNext); + result = device->funcs.p_vkMapMemory(device->device, info.memory, info.offset, + info.size, info.flags, data); + } #ifdef _WIN64 if (NtCurrentTeb()->WowTebOffset && result == VK_SUCCESS && (UINT_PTR)*data >> 32) @@ -2911,13 +2936,36 @@ VkResult wine_vkMapMemory(VkDevice handle, VkDeviceMemory memory_handle, VkDevic return result; } -void wine_vkUnmapMemory(VkDevice handle, VkDeviceMemory memory_handle) +void wine_vkUnmapMemory(VkDevice device, VkDeviceMemory memory) +{ + const VkMemoryUnmapInfoKHR info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_UNMAP_INFO_KHR, + .memory = memory, + }; + + wine_vkUnmapMemory2KHR(device, &info); +} + +VkResult wine_vkUnmapMemory2KHR(VkDevice handle, const VkMemoryUnmapInfoKHR *unmap_info) { struct wine_device *device = wine_device_from_handle(handle); - struct wine_device_memory *memory = wine_device_memory_from_handle(memory_handle); + struct wine_device_memory *memory = wine_device_memory_from_handle(unmap_info->memory); + VkMemoryUnmapInfoKHR info; - if (!memory->mapping) + if (memory->mapping) + return VK_SUCCESS; + + if (!device->funcs.p_vkUnmapMemory2KHR) + { + assert(!unmap_info->pNext); device->funcs.p_vkUnmapMemory(device->device, memory->memory); + return VK_SUCCESS; + } + + info = *unmap_info; + info.memory = memory->memory; + return device->funcs.p_vkUnmapMemory2KHR(device->device, &info); } VkResult wine_vkCreateBuffer(VkDevice handle, const VkBufferCreateInfo *create_info, From acec0fba45dd5a17a11cfe0f07342eb02fc06a6f Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Thu, 4 May 2023 12:14:05 +0200 Subject: [PATCH 449/758] winevulkan: Update to VK spec version 1.3.250. (cherry picked from commit e32711dd2fee94cba3ccd145397fd1904e6a397f) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index cf67c2c6764..760b1b68458 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.246" +VK_XML_VERSION = "1.3.250" WINE_VK_VERSION = (1, 3) # Filenames to create. From 393f532e285756cebbc76a9cc2f40d31acdda624 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Tue, 30 May 2023 08:50:05 +0200 Subject: [PATCH 450/758] winevulkan: Update to VK spec version 1.3.251. (cherry picked from commit 8190aa25a00e91372090b1ba722b95defbcbed06) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 760b1b68458..7da5deed31c 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.250" +VK_XML_VERSION = "1.3.251" WINE_VK_VERSION = (1, 3) # Filenames to create. From f705976ddfe24a6fbc46c9789210dd0b47e6a0a3 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 16 Jun 2023 17:48:29 +0200 Subject: [PATCH 451/758] winevulkan: Update to VK spec version 1.3.254. Link: https://gitlab.winehq.org/wine/wine/-/merge_requests/3078 --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 7da5deed31c..9de05b75662 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.251" +VK_XML_VERSION = "1.3.254" WINE_VK_VERSION = (1, 3) # Filenames to create. From 3884e5c8e1a08bb81b5bc78ed23a35f48c838f2f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 16 Jun 2023 22:01:19 +0300 Subject: [PATCH 452/758] winevulkan: Update vk.xml to 1.3.254. --- dlls/winevulkan/vk.xml | 3904 ++++++++++++++++++++++++++++++---------- 1 file changed, 2995 insertions(+), 909 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index f116cbff9a5..0c4bb61609f 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -1,7 +1,7 @@ -Copyright 2015-2022 The Khronos Group Inc. +Copyright 2015-2023 The Khronos Group Inc. SPDX-License-Identifier: Apache-2.0 OR MIT @@ -32,12 +32,13 @@ branch of the member gitlab server. + - + @@ -66,14 +67,15 @@ branch of the member gitlab server. - + - + - + + @@ -90,6 +92,8 @@ branch of the member gitlab server. + + In the current header structure, each platform's interfaces are confined to a platform-specific header (vulkan_xlib.h, @@ -130,41 +134,59 @@ branch of the member gitlab server. + + + + + + - // DEPRECATED: This define is deprecated. VK_MAKE_API_VERSION should be used instead. + // DEPRECATED: This define is deprecated. VK_MAKE_API_VERSION should be used instead. #define VK_MAKE_VERSION(major, minor, patch) \ - ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) - // DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead. -#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) - // DEPRECATED: This define is deprecated. VK_API_VERSION_MINOR should be used instead. -#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) - // DEPRECATED: This define is deprecated. VK_API_VERSION_PATCH should be used instead. + ((((uint32_t)(major)) << 22U) | (((uint32_t)(minor)) << 12U) | ((uint32_t)(patch))) + // DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead. +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22U) + // DEPRECATED: This define is deprecated. VK_API_VERSION_MINOR should be used instead. +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12U) & 0x3FFU) + // DEPRECATED: This define is deprecated. VK_API_VERSION_PATCH should be used instead. #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) #define VK_MAKE_API_VERSION(variant, major, minor, patch) \ - ((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) - #define VK_API_VERSION_VARIANT(version) ((uint32_t)(version) >> 29) - #define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU) - #define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) + ((((uint32_t)(variant)) << 29U) | (((uint32_t)(major)) << 22U) | (((uint32_t)(minor)) << 12U) | ((uint32_t)(patch))) + #define VK_API_VERSION_VARIANT(version) ((uint32_t)(version) >> 29U) + #define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22U) & 0x7FU) + #define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12U) & 0x3FFU) #define VK_API_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) + // Vulkan SC variant number +#define VKSC_API_VARIANT 1 + // DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. -//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 - // Vulkan 1.0 version number +//#define VK_API_VERSION VK_MAKE_API_VERSION(0, 1, 0, 0) // Patch version should always be set to 0 + // Vulkan 1.0 version number #define VK_API_VERSION_1_0 VK_MAKE_API_VERSION(0, 1, 0, 0)// Patch version should always be set to 0 - // Vulkan 1.1 version number + // Vulkan 1.1 version number #define VK_API_VERSION_1_1 VK_MAKE_API_VERSION(0, 1, 1, 0)// Patch version should always be set to 0 - // Vulkan 1.2 version number + // Vulkan 1.2 version number #define VK_API_VERSION_1_2 VK_MAKE_API_VERSION(0, 1, 2, 0)// Patch version should always be set to 0 // Vulkan 1.3 version number #define VK_API_VERSION_1_3 VK_MAKE_API_VERSION(0, 1, 3, 0)// Patch version should always be set to 0 - // Version of this file -#define VK_HEADER_VERSION 235 - // Complete version of this file + // Vulkan SC 1.0 version number +#define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 + + // Version of this file +#define VK_HEADER_VERSION 254 + // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) + // Version of this file +#define VK_HEADER_VERSION 12 + // Complete version of this file +#define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, VK_HEADER_VERSION) - + #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; + +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* (object); #ifndef VK_USE_64_BIT_PTR_DEFINES @@ -189,7 +211,7 @@ branch of the member gitlab server. #ifndef VK_NULL_HANDLE #define VK_NULL_HANDLE 0 #endif - + #ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE #if (VK_USE_64_BIT_PTR_DEFINES==1) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; @@ -197,6 +219,14 @@ branch of the member gitlab server. #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; #endif #endif + +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *(object); + #else + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t (object); + #endif +#endif struct ANativeWindow; struct AHardwareBuffer; @@ -267,9 +297,11 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkSamplerCreateFlags; typedef VkFlags VkPipelineLayoutCreateFlags; typedef VkFlags VkPipelineCacheCreateFlags; - typedef VkFlags VkPipelineDepthStencilStateCreateFlags; + typedef VkFlags VkPipelineDepthStencilStateCreateFlags; + typedef VkFlags VkPipelineDepthStencilStateCreateFlags; typedef VkFlags VkPipelineDynamicStateCreateFlags; - typedef VkFlags VkPipelineColorBlendStateCreateFlags; + typedef VkFlags VkPipelineColorBlendStateCreateFlags; + typedef VkFlags VkPipelineColorBlendStateCreateFlags; typedef VkFlags VkPipelineMultisampleStateCreateFlags; typedef VkFlags VkPipelineRasterizationStateCreateFlags; typedef VkFlags VkPipelineViewportStateCreateFlags; @@ -307,6 +339,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkCommandBufferUsageFlags; typedef VkFlags VkQueryPipelineStatisticFlags; typedef VkFlags VkMemoryMapFlags; + typedef VkFlags VkMemoryUnmapFlagsKHR; typedef VkFlags VkImageAspectFlags; typedef VkFlags VkSparseMemoryBindFlags; typedef VkFlags VkSparseImageFormatFlags; @@ -342,6 +375,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkPipelineCompilerControlFlagsAMD; typedef VkFlags VkShaderCorePropertiesFlagsAMD; typedef VkFlags VkDeviceDiagnosticsConfigFlagsNV; + typedef VkFlags VkRefreshObjectFlagsKHR; typedef VkFlags64 VkAccessFlags2; typedef VkFlags64 VkPipelineStageFlags2; @@ -355,7 +389,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkBuildMicromapFlagsEXT; typedef VkFlags VkMicromapCreateFlagsEXT; - + typedef VkFlags VkDirectDriverLoadingFlagsLUNARG; WSI extensions typedef VkFlags VkCompositeAlphaFlagsKHR; @@ -441,6 +475,9 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkOpticalFlowUsageFlagsNV; typedef VkFlags VkOpticalFlowSessionCreateFlagsNV; typedef VkFlags VkOpticalFlowExecuteFlagsNV; + typedef VkFlags VkPresentScalingFlagsEXT; + typedef VkFlags VkPresentGravityFlagsEXT; + typedef VkFlags VkShaderCreateFlagsEXT; Video Core extension typedef VkFlags VkVideoCodecOperationFlagsKHR; @@ -457,29 +494,30 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkVideoDecodeFlagsKHR; Video Decode H.264 extension - typedef VkFlags VkVideoDecodeH264PictureLayoutFlagsEXT; + typedef VkFlags VkVideoDecodeH264PictureLayoutFlagsKHR; Video Encode Core extension typedef VkFlags VkVideoEncodeFlagsKHR; typedef VkFlags VkVideoEncodeUsageFlagsKHR; typedef VkFlags VkVideoEncodeContentFlagsKHR; typedef VkFlags VkVideoEncodeCapabilityFlagsKHR; + typedef VkFlags VkVideoEncodeFeedbackFlagsKHR; typedef VkFlags VkVideoEncodeRateControlFlagsKHR; typedef VkFlags VkVideoEncodeRateControlModeFlagsKHR; typedef VkFlags VkVideoChromaSubsamplingFlagsKHR; typedef VkFlags VkVideoComponentBitDepthFlagsKHR; Video Encode H.264 extension - typedef VkFlags VkVideoEncodeH264CapabilityFlagsEXT; - typedef VkFlags VkVideoEncodeH264InputModeFlagsEXT; - typedef VkFlags VkVideoEncodeH264OutputModeFlagsEXT; + typedef VkFlags VkVideoEncodeH264CapabilityFlagsEXT; + typedef VkFlags VkVideoEncodeH264StdFlagsEXT; + typedef VkFlags VkVideoEncodeH264RateControlFlagsEXT; Video Encode H.265 extension - typedef VkFlags VkVideoEncodeH265CapabilityFlagsEXT; - typedef VkFlags VkVideoEncodeH265InputModeFlagsEXT; - typedef VkFlags VkVideoEncodeH265OutputModeFlagsEXT; - typedef VkFlags VkVideoEncodeH265CtbSizeFlagsEXT; - typedef VkFlags VkVideoEncodeH265TransformBlockSizeFlagsEXT; + typedef VkFlags VkVideoEncodeH265CapabilityFlagsEXT; + typedef VkFlags VkVideoEncodeH265StdFlagsEXT; + typedef VkFlags VkVideoEncodeH265RateControlFlagsEXT; + typedef VkFlags VkVideoEncodeH265CtbSizeFlagsEXT; + typedef VkFlags VkVideoEncodeH265TransformBlockSizeFlagsEXT; Types which can be void pointers or class pointers, selected at compile time VK_DEFINE_HANDLE(VkInstance) @@ -524,12 +562,13 @@ typedef void* MTLSharedEvent_id; VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCuFunctionNVX) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkOpticalFlowSessionNV) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkMicromapEXT) + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderEXT) WSI extensions VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayModeKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) - VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugUtilsMessengerEXT) @@ -537,6 +576,9 @@ typedef void* MTLSharedEvent_id; VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkVideoSessionKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkVideoSessionParametersKHR) + VK_NV_external_sci_sync2 + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphoreSciSyncPoolNV) + Types generated from corresponding enums tags below @@ -690,6 +732,13 @@ typedef void* MTLSharedEvent_id; + + + + + + + @@ -700,6 +749,7 @@ typedef void* MTLSharedEvent_id; + @@ -724,6 +774,11 @@ typedef void* MTLSharedEvent_id; + + + + + WSI extensions @@ -795,6 +850,8 @@ typedef void* MTLSharedEvent_id; + + Enumerated types in the header, but not used by the API @@ -818,7 +875,7 @@ typedef void* MTLSharedEvent_id; Video H.264 Decode extensions - + Video H.265 Decode extensions @@ -827,21 +884,20 @@ typedef void* MTLSharedEvent_id; + Video H.264 Encode extensions - - - - + + + Video H.265 Encode extensions - - - - - - + + + + + The PFN_vk*Function types are used by VkAllocationCallbacks below typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( @@ -890,11 +946,26 @@ typedef void* MTLSharedEvent_id; const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); + The PFN_vkFaultCallbackFunction type is used by VKSC_VERSION_1_0 + typedef void (VKAPI_PTR *PFN_vkFaultCallbackFunction)( + VkBool32 unrecordedFaults, + uint32_t faultCount, + const VkFaultData* pFaults); + The PFN_vkDeviceMemoryReportCallbackEXT type is used by the VK_EXT_device_memory_report extension typedef void (VKAPI_PTR *PFN_vkDeviceMemoryReportCallbackEXT)( const VkDeviceMemoryReportCallbackDataEXT* pCallbackData, void* pUserData); + The PFN_vkGetInstanceProcAddrLUNARG type is used by the + VkDirectDriverLoadingInfoLUNARG structure. + We cannot introduce an explicit dependency on the + equivalent PFN_vkGetInstanceProcAddr type, even though + it is implicitly generated in the C header, because + that results in multiple definitions. + typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetInstanceProcAddrLUNARG)( + VkInstance instance, const char* pName); + Struct types VkStructureType sType @@ -997,8 +1068,8 @@ typedef void* MTLSharedEvent_id; VkDeviceCreateFlags flags uint32_t queueCreateInfoCount const VkDeviceQueueCreateInfo* pQueueCreateInfos - uint32_t enabledLayerCount - const char* const* ppEnabledLayerNamesOrdered list of layer names to be enabled + uint32_t enabledLayerCount + const char* const* ppEnabledLayerNamesOrdered list of layer names to be enabled uint32_t enabledExtensionCount const char* const* ppEnabledExtensionNames const VkPhysicalDeviceFeatures* pEnabledFeatures @@ -1191,7 +1262,7 @@ typedef void* MTLSharedEvent_id; const uint32_t* pQueueFamilyIndicesArray of queue family indices to share across VkImageLayout initialLayoutInitial image layout for all subresources - + VkDeviceSize offsetSpecified in bytes VkDeviceSize sizeSpecified in bytes VkDeviceSize rowPitchSpecified in bytes @@ -1354,8 +1425,9 @@ typedef void* MTLSharedEvent_id; const void* pNext VkPipelineShaderStageCreateFlags flags VkShaderStageFlagBits stageShader stage - VkShaderModule moduleModule containing entry point - const char* pNameNull-terminated entry point name + VkShaderModule moduleModule containing entry point + const char* pNameNull-terminated entry point name + const char* pNameNull-terminated entry point name const VkSpecializationInfo* pSpecializationInfo @@ -1489,8 +1561,9 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkPipelineCreateFlags flagsPipeline creation flags - uint32_t stageCount - const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage + uint32_t stageCount + const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage + const VkPipelineShaderStageCreateInfo* pStagesOne entry for each active shader stage const VkPipelineVertexInputStateCreateInfo* pVertexInputState const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState const VkPipelineTessellationStateCreateInfo* pTessellationState @@ -1510,7 +1583,8 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkPipelineCacheCreateFlags flags - size_t initialDataSizeSize of initial data to populate cache, in bytes + size_t initialDataSizeSize of initial data to populate cache, in bytes + size_t initialDataSizeSize of initial data to populate cache, in bytes const void* pInitialDataInitial data to populate cache @@ -1521,6 +1595,30 @@ typedef void* MTLSharedEvent_id; uint32_t deviceID uint8_t pipelineCacheUUID[VK_UUID_SIZE] + + The fields in this structure are non-normative since structure packing is implementation-defined in C. The specification defines the normative layout. + uint64_t codeSize + uint64_t codeOffset + + + The fields in this structure are non-normative since structure packing is implementation-defined in C. The specification defines the normative layout. + uint8_t pipelineIdentifier[VK_UUID_SIZE] + uint64_t pipelineMemorySize + uint64_t jsonSize + uint64_t jsonOffset + uint32_t stageIndexCount + uint32_t stageIndexStride + uint64_t stageIndexOffset + + + The fields in this structure are non-normative since structure packing is implementation-defined in C. The specification defines the normative layout. + VkPipelineCacheHeaderVersionOne headerVersionOne + VkPipelineCacheValidationVersion validationVersion + uint32_t implementationData + uint32_t pipelineIndexCount + uint32_t pipelineIndexStride + uint64_t pipelineIndexOffset + VkShaderStageFlags stageFlagsWhich stages use the range uint32_t offsetStart of the range, in bytes @@ -2069,7 +2167,8 @@ typedef void* MTLSharedEvent_id; VkCompositeAlphaFlagBitsKHR compositeAlphaThe alpha blending mode used when compositing this surface with other surfaces in the window system VkPresentModeKHR presentModeWhich presentation mode to use for presents on this swap chain VkBool32 clippedSpecifies whether presentable images may be affected by window clip regions - VkSwapchainKHR oldSwapchainExisting swap chain to replace, if any + VkSwapchainKHR oldSwapchainExisting swap chain to replace, if any + VkSwapchainKHR oldSwapchainExisting swap chain to replace, if any VkStructureType sType @@ -2102,6 +2201,14 @@ typedef void* MTLSharedEvent_id; uint32_t disabledValidationFeatureCountNumber of validation features to disable const VkValidationFeatureDisableEXT* pDisabledValidationFeaturesValidation features to disable + + VkStructureType sType + const void* pNext + uint32_t vendorID + uint32_t deviceID + uint32_t key + uint64_t value + VkStructureType sType const void* pNext @@ -2173,6 +2280,35 @@ typedef void* MTLSharedEvent_id; const SECURITY_ATTRIBUTES* pAttributes DWORD dwAccess + + VkStructureType sType + const void* pNext + NvSciBufAttrList pAttributes + + + VkStructureType sType + const void* pNext + VkExternalMemoryHandleTypeFlagBits handleType + NvSciBufObj handle + + + VkStructureType sType + const void* pNext + VkDeviceMemory memory + VkExternalMemoryHandleTypeFlagBits handleType + + + VkStructureType sType + const void* pNext + uint32_t memoryTypeBits + + + VkStructureType sType + void* pNext + VkBool32 sciBufImport + VkBool32 sciBufExport + + VkStructureType sType const void* pNext @@ -2684,6 +2820,80 @@ typedef void* MTLSharedEvent_id; VkFence fence VkExternalFenceHandleTypeFlagBits handleType + + VkStructureType sType + const void* pNext + NvSciSyncAttrList pAttributes + + + VkStructureType sType + const void* pNext + VkFence fence + VkExternalFenceHandleTypeFlagBits handleType + void* handle + + + VkStructureType sType + const void* pNext + VkFence fence + VkExternalFenceHandleTypeFlagBits handleType + + + VkStructureType sType + const void* pNext + NvSciSyncAttrList pAttributes + + + VkStructureType sType + const void* pNext + VkSemaphore semaphore + VkExternalSemaphoreHandleTypeFlagBits handleType + void* handle + + + VkStructureType sType + const void* pNext + VkSemaphore semaphore + VkExternalSemaphoreHandleTypeFlagBits handleType + + + VkStructureType sType + const void* pNext + VkSciSyncClientTypeNV clientType + VkSciSyncPrimitiveTypeNV primitiveType + + + VkStructureType sType + void* pNext + VkBool32 sciSyncFence + VkBool32 sciSyncSemaphore + VkBool32 sciSyncImport + VkBool32 sciSyncExport + + + VkStructureType sType + void* pNext + VkBool32 sciSyncFence + VkBool32 sciSyncSemaphore2 + VkBool32 sciSyncImport + VkBool32 sciSyncExport + + + VkStructureType sType + const void* pNext + NvSciSyncObj handle + + + VkStructureType sType + const void* pNext + VkSemaphoreSciSyncPoolNV semaphorePool + const NvSciSyncFence* pFence + + + VkStructureType sType + const void* pNext + uint32_t semaphoreSciSyncPoolRequestCount + VkStructureType sType void* pNext @@ -3166,6 +3376,12 @@ typedef void* MTLSharedEvent_id; const void* pNext VkImageUsageFlags usage + + VkStructureType sType + const void* pNext + uint32_t sliceOffset + uint32_t sliceCount + VkStructureType sType @@ -4820,6 +5036,11 @@ typedef void* MTLSharedEvent_id; const void* pNext uint32_t counterPassIndexIndex for which counter pass to submit + + VkStructureType sType + const void* pNext + uint32_t maxPerformanceQueriesPerPoolMaximum number of VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR queries in a query pool + VkStructureType sType const void* pNext @@ -5031,12 +5252,13 @@ typedef void* MTLSharedEvent_id; VkShaderStageFlags requiredSubgroupSizeStagesThe shader stages that support specifying a subgroup size - + VkStructureType sType void* pNext uint32_t requiredSubgroupSize + VkStructureType sType void* pNext @@ -5048,6 +5270,14 @@ typedef void* MTLSharedEvent_id; void* pNext uint32_t maxSubpassShadingWorkgroupSizeAspectRatio + + VkStructureType sType + void* pNext + uint32_t maxWorkGroupCount[3] + uint32_t maxWorkGroupSize[3] + uint32_t maxOutputClusterCount + VkDeviceSize indirectBufferOffsetAlignment + VkStructureType sType const void* pNext @@ -5309,6 +5539,19 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 deviceCoherentMemory + + VkStructureType sType + void* pNext + VkFaultLevel faultLevel + VkFaultType faultType + + + VkStructureType sType + const void* pNext + uint32_t faultCount + VkFaultData*pFaults + PFN_vkFaultCallbackFunction pfnFaultCallback + VkStructureType sType void* pNext @@ -5486,6 +5729,17 @@ typedef void* MTLSharedEvent_id; uint32_t libraryCount const VkPipeline* pLibraries + + VkObjectType objectType + uint64_t objectHandle + VkRefreshObjectFlagsKHR flags + + + VkStructureType sType + const void* pNext + uint32_t objectCount + const VkRefreshObjectKHR* pObjects + VkStructureType sType void* pNext @@ -5579,6 +5833,13 @@ typedef void* MTLSharedEvent_id; const void* pNext VkDeviceDiagnosticsConfigFlagsNV flags + + VkStructureType sType + const void* pNext + uint8_t pipelineIdentifier[VK_UUID_SIZE] + VkPipelineMatchControl matchControl + VkDeviceSize poolEntrySize + VkStructureType sType void* pNext @@ -5652,6 +5913,12 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 subpassShading + + VkStructureType sType + void*pNext + VkBool32 clustercullingShader + VkBool32 multiviewClusterCullingShader + VkStructureType sType const void* pNext @@ -5853,6 +6120,16 @@ typedef void* MTLSharedEvent_id; VkBool32 image2DViewOf3D VkBool32 sampler2DViewOf3D + + VkStructureType sType + void* pNext + VkBool32 imageSlicedViewOf3D + + + VkStructureType sType + void* pNext + VkBool32 attachmentFeedbackLoopDynamicState + VkStructureType sType void* pNext @@ -6013,6 +6290,97 @@ typedef void* MTLSharedEvent_id; VkBool32 synchronization2 + + VkStructureType sType + void* pNext + VkBool32 deviceNoDynamicHostAllocations + VkBool32 deviceDestroyFreesMemory + VkBool32 commandPoolMultipleCommandBuffersRecording + VkBool32 commandPoolResetCommandBuffer + VkBool32 commandBufferSimultaneousUse + VkBool32 secondaryCommandBufferNullOrImagelessFramebuffer + VkBool32 recycleDescriptorSetMemory + VkBool32 recyclePipelineMemory + uint32_t maxRenderPassSubpasses + uint32_t maxRenderPassDependencies + uint32_t maxSubpassInputAttachments + uint32_t maxSubpassPreserveAttachments + uint32_t maxFramebufferAttachments + uint32_t maxDescriptorSetLayoutBindings + uint32_t maxQueryFaultCount + uint32_t maxCallbackFaultCount + uint32_t maxCommandPoolCommandBuffers + VkDeviceSize maxCommandBufferSize + + + VkStructureType sType + const void* pNext + VkDeviceSize poolEntrySize + uint32_t poolEntryCount + + + VkStructureType sType + const void* pNext + uint32_t pipelineCacheCreateInfoCount + const VkPipelineCacheCreateInfo* pPipelineCacheCreateInfos + uint32_t pipelinePoolSizeCount + const VkPipelinePoolSize* pPipelinePoolSizes + uint32_t semaphoreRequestCount + uint32_t commandBufferRequestCount + uint32_t fenceRequestCount + uint32_t deviceMemoryRequestCount + uint32_t bufferRequestCount + uint32_t imageRequestCount + uint32_t eventRequestCount + uint32_t queryPoolRequestCount + uint32_t bufferViewRequestCount + uint32_t imageViewRequestCount + uint32_t layeredImageViewRequestCount + uint32_t pipelineCacheRequestCount + uint32_t pipelineLayoutRequestCount + uint32_t renderPassRequestCount + uint32_t graphicsPipelineRequestCount + uint32_t computePipelineRequestCount + uint32_t descriptorSetLayoutRequestCount + uint32_t samplerRequestCount + uint32_t descriptorPoolRequestCount + uint32_t descriptorSetRequestCount + uint32_t framebufferRequestCount + uint32_t commandPoolRequestCount + uint32_t samplerYcbcrConversionRequestCount + uint32_t surfaceRequestCount + uint32_t swapchainRequestCount + uint32_t displayModeRequestCount + uint32_t subpassDescriptionRequestCount + uint32_t attachmentDescriptionRequestCount + uint32_t descriptorSetLayoutBindingRequestCount + uint32_t descriptorSetLayoutBindingLimit + uint32_t maxImageViewMipLevels + uint32_t maxImageViewArrayLayers + uint32_t maxLayeredImageViewMipLevels + uint32_t maxOcclusionQueriesPerPool + uint32_t maxPipelineStatisticsQueriesPerPool + uint32_t maxTimestampQueriesPerPool + uint32_t maxImmutableSamplersPerDescriptorSetLayout + + + VkStructureType sType + const void* pNext + VkDeviceSize commandPoolReservedSize + uint32_t commandPoolMaxCommandBuffers + + + VkStructureType sType + void* pNext + VkDeviceSize commandPoolAllocated + VkDeviceSize commandPoolReservedSize + VkDeviceSize commandBufferAllocated + + + VkStructureType sType + void* pNext + VkBool32 shaderAtomicInstructions + VkStructureType sType void* pNext @@ -6063,7 +6431,7 @@ typedef void* MTLSharedEvent_id; const VkVideoProfileInfoKHR* pProfiles - VkStructureType sType + VkStructureType sType const void* pNext VkImageUsageFlags imageUsage @@ -6173,44 +6541,44 @@ typedef void* MTLSharedEvent_id; - - VkStructureType sType + + VkStructureType sType const void* pNext StdVideoH264ProfileIdc stdProfileIdc - VkVideoDecodeH264PictureLayoutFlagBitsEXT pictureLayout + VkVideoDecodeH264PictureLayoutFlagBitsKHR pictureLayout - - VkStructureType sType + + VkStructureType sType void* pNext StdVideoH264LevelIdc maxLevelIdc VkOffset2D fieldOffsetGranularity - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t stdSPSCount const StdVideoH264SequenceParameterSet* pStdSPSs uint32_t stdPPSCount const StdVideoH264PictureParameterSet* pStdPPSsList of Picture Parameters associated with the spsStd, above - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t maxStdSPSCount uint32_t maxStdPPSCount - const VkVideoDecodeH264SessionParametersAddInfoEXT* pParametersAddInfo + const VkVideoDecodeH264SessionParametersAddInfoKHR* pParametersAddInfo - - VkStructureType sType + + VkStructureType sType const void* pNext const StdVideoDecodeH264PictureInfo* pStdPictureInfo uint32_t sliceCount const uint32_t* pSliceOffsets - - VkStructureType sType + + VkStructureType sType const void* pNext const StdVideoDecodeH264ReferenceInfo* pStdReferenceInfo @@ -6238,18 +6606,18 @@ typedef void* MTLSharedEvent_id; - - VkStructureType sType + + VkStructureType sType const void* pNext StdVideoH265ProfileIdc stdProfileIdc - - VkStructureType sType + + VkStructureType sType void* pNext StdVideoH265LevelIdc maxLevelIdc - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t stdVPSCount const StdVideoH265VideoParameterSet* pStdVPSs @@ -6258,23 +6626,23 @@ typedef void* MTLSharedEvent_id; uint32_t stdPPSCount const StdVideoH265PictureParameterSet* pStdPPSsList of Picture Parameters associated with the spsStd, above - - VkStructureType sType + + VkStructureType sType const void* pNext uint32_t maxStdVPSCount uint32_t maxStdSPSCount uint32_t maxStdPPSCount - const VkVideoDecodeH265SessionParametersAddInfoEXT* pParametersAddInfo + const VkVideoDecodeH265SessionParametersAddInfoKHR* pParametersAddInfo - - VkStructureType sType - const void* pNext - StdVideoDecodeH265PictureInfo* pStdPictureInfo - uint32_t sliceCount - const uint32_t* pSliceOffsets + + VkStructureType sType + const void* pNext + const StdVideoDecodeH265PictureInfo* pStdPictureInfo + uint32_t sliceSegmentCount + const uint32_t* pSliceSegmentOffsets - - VkStructureType sType + + VkStructureType sType const void* pNext const StdVideoDecodeH265ReferenceInfo* pStdReferenceInfo @@ -6303,6 +6671,16 @@ typedef void* MTLSharedEvent_id; const void* pNext uint32_t updateSequenceCount + + VkStructureType sType + const void* pNext + VkVideoSessionParametersKHR videoSessionParameters + + + VkStructureType sType + void* pNext + VkBool32 hasOverrides + VkStructureType sType const void* pNext @@ -6333,69 +6711,113 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkVideoEncodeFlagsKHR flags - uint32_t qualityLevel - VkBuffer dstBitstreamBuffer - VkDeviceSize dstBitstreamBufferOffset - VkDeviceSize dstBitstreamBufferMaxRange + VkBuffer dstBuffer + VkDeviceSize dstBufferOffset + VkDeviceSize dstBufferRange VkVideoPictureResourceInfoKHR srcPictureResource const VkVideoReferenceSlotInfoKHR* pSetupReferenceSlot uint32_t referenceSlotCount const VkVideoReferenceSlotInfoKHR* pReferenceSlots uint32_t precedingExternallyEncodedBytes - + + VkStructureType sType + const void* pNext + VkVideoEncodeFeedbackFlagsKHR encodeFeedbackFlags + + + VkStructureType sType + const void* pNext + uint32_t qualityLevel + + + VkStructureType sType + const void* pNext + const VkVideoProfileInfoKHR* pVideoProfile + uint32_t qualityLevel + + + VkStructureType sType + void* pNext + VkVideoEncodeRateControlModeFlagBitsKHR preferredRateControlMode + uint32_t preferredRateControlLayerCount + + VkStructureType sType - const void* pNext + const void* pNext VkVideoEncodeRateControlFlagsKHR flags - VkVideoEncodeRateControlModeFlagBitsKHR rateControlMode - uint8_t layerCount - const VkVideoEncodeRateControlLayerInfoKHR* pLayerConfigs + VkVideoEncodeRateControlModeFlagBitsKHR rateControlMode + uint32_t layerCount + const VkVideoEncodeRateControlLayerInfoKHR* pLayers + uint32_t virtualBufferSizeInMs + uint32_t initialVirtualBufferSizeInMs - + VkStructureType sType - const void* pNext - uint32_t averageBitrate - uint32_t maxBitrate - uint32_t frameRateNumerator - uint32_t frameRateDenominator - uint32_t virtualBufferSizeInMs - uint32_t initialVirtualBufferSizeInMs + const void* pNext + uint64_t averageBitrate + uint64_t maxBitrate + uint32_t frameRateNumerator + uint32_t frameRateDenominator VkStructureType sType void* pNext VkVideoEncodeCapabilityFlagsKHR flags VkVideoEncodeRateControlModeFlagsKHR rateControlModes - uint8_t rateControlLayerCount - uint8_t qualityLevelCount - VkExtent2D inputImageDataFillAlignment + uint32_t maxRateControlLayers + uint64_t maxBitrate + uint32_t maxQualityLevels + VkExtent2D encodeInputPictureGranularity + VkVideoEncodeFeedbackFlagsKHR supportedEncodeFeedbackFlags VkStructureType sType void* pNext - VkVideoEncodeH264CapabilityFlagsEXT flags - VkVideoEncodeH264InputModeFlagsEXT inputModeFlags - VkVideoEncodeH264OutputModeFlagsEXT outputModeFlags - uint8_t maxPPictureL0ReferenceCount - uint8_t maxBPictureL0ReferenceCount - uint8_t maxL1ReferenceCount - VkBool32 motionVectorsOverPicBoundariesFlag - uint32_t maxBytesPerPicDenom - uint32_t maxBitsPerMbDenom - uint32_t log2MaxMvLengthHorizontal - uint32_t log2MaxMvLengthVertical + VkVideoEncodeH264CapabilityFlagsEXT flags + StdVideoH264LevelIdc maxLevelIdc + uint32_t maxSliceCount + uint32_t maxPPictureL0ReferenceCount + uint32_t maxBPictureL0ReferenceCount + uint32_t maxL1ReferenceCount + uint32_t maxTemporalLayerCount + VkBool32 expectDyadicTemporalLayerPattern + int32_t minQp + int32_t maxQp + VkBool32 prefersGopRemainingFrames + VkBool32 requiresGopRemainingFrames + VkVideoEncodeH264StdFlagsEXT stdSyntaxFlags + + + VkStructureType sType + void* pNext + VkVideoEncodeH264RateControlFlagsEXT preferredRateControlFlags + uint32_t preferredGopFrameCount + uint32_t preferredIdrPeriod + uint32_t preferredConsecutiveBFrameCount + uint32_t preferredTemporalLayerCount + VkVideoEncodeH264QpEXT preferredConstantQp + uint32_t preferredMaxL0ReferenceCount + uint32_t preferredMaxL1ReferenceCount + VkBool32 preferredStdEntropyCodingModeFlag #include "vk_video/vulkan_video_codec_h264std_encode.h" - + + + VkStructureType sType + const void* pNext + VkBool32 useMaxLevelIdc + StdVideoH264LevelIdc maxLevelIdc + VkStructureType sType const void* pNext @@ -6406,41 +6828,37 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - const void* pNext + const void* pNext uint32_t maxStdSPSCount uint32_t maxStdPPSCount const VkVideoEncodeH264SessionParametersAddInfoEXT* pParametersAddInfo - + + VkStructureType sType + const void* pNext + VkBool32 writeStdSPS + VkBool32 writeStdPPS + uint32_t stdSPSId + uint32_t stdPPSId + + + VkStructureType sType + void* pNext + VkBool32 hasStdSPSOverrides + VkBool32 hasStdPPSOverrides + + VkStructureType sType - const void* pNext - int8_t slotIndex + const void* pNext const StdVideoEncodeH264ReferenceInfo* pStdReferenceInfo - - VkStructureType sType + + VkStructureType sType const void* pNext - const VkVideoEncodeH264ReferenceListsInfoEXT* pReferenceFinalLists uint32_t naluSliceEntryCount const VkVideoEncodeH264NaluSliceInfoEXT* pNaluSliceEntries - const StdVideoEncodeH264PictureInfo* pCurrentPictureInfo - - - VkStructureType sType - const void* pNext - uint8_t referenceList0EntryCount - const VkVideoEncodeH264DpbSlotInfoEXT* pReferenceList0Entries - uint8_t referenceList1EntryCount - const VkVideoEncodeH264DpbSlotInfoEXT* pReferenceList1Entries - const StdVideoEncodeH264RefMemMgmtCtrlOperations* pMemMgmtCtrlOperations - - - VkStructureType sType - const void* pNext - uint8_t spsId - VkBool32 emitSpsEnable - uint32_t ppsIdEntryCount - const uint8_t* ppsIdEntries + const StdVideoEncodeH264PictureInfo* pStdPictureInfo + VkBool32 generatePrefixNalu VkStructureType sType @@ -6450,18 +6868,17 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - uint32_t mbCount - const VkVideoEncodeH264ReferenceListsInfoEXT* pReferenceFinalLists - const StdVideoEncodeH264SliceHeader* pSliceHeaderStd + int32_t constantQp + const StdVideoEncodeH264SliceHeader* pStdSliceHeader - + VkStructureType sType const void* pNext + VkVideoEncodeH264RateControlFlagsEXT flags uint32_t gopFrameCount uint32_t idrPeriod uint32_t consecutiveBFrameCount - VkVideoEncodeH264RateControlStructureEXT rateControlStructure - uint8_t temporalLayerCount + uint32_t temporalLayerCount int32_t qpI @@ -6473,12 +6890,17 @@ typedef void* MTLSharedEvent_id; uint32_t framePSize uint32_t frameBSize - + + VkStructureType sType + const void* pNext + VkBool32 useGopRemainingFrames + uint32_t gopRemainingI + uint32_t gopRemainingP + uint32_t gopRemainingB + + VkStructureType sType const void* pNext - uint8_t temporalLayerId - VkBool32 useInitialRcQp - VkVideoEncodeH264QpEXT initialRcQp VkBool32 useMinQp VkVideoEncodeH264QpEXT minQp VkBool32 useMaxQp @@ -6489,36 +6911,50 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkVideoEncodeH265CapabilityFlagsEXT flags - VkVideoEncodeH265InputModeFlagsEXT inputModeFlags - VkVideoEncodeH265OutputModeFlagsEXT outputModeFlags + VkVideoEncodeH265CapabilityFlagsEXT flags + StdVideoH265LevelIdc maxLevelIdc + uint32_t maxSliceSegmentCount + VkExtent2D maxTiles VkVideoEncodeH265CtbSizeFlagsEXT ctbSizes VkVideoEncodeH265TransformBlockSizeFlagsEXT transformBlockSizes - uint8_t maxPPictureL0ReferenceCount - uint8_t maxBPictureL0ReferenceCount - uint8_t maxL1ReferenceCount - uint8_t maxSubLayersCount - uint8_t minLog2MinLumaCodingBlockSizeMinus3 - uint8_t maxLog2MinLumaCodingBlockSizeMinus3 - uint8_t minLog2MinLumaTransformBlockSizeMinus2 - uint8_t maxLog2MinLumaTransformBlockSizeMinus2 - uint8_t minMaxTransformHierarchyDepthInter - uint8_t maxMaxTransformHierarchyDepthInter - uint8_t minMaxTransformHierarchyDepthIntra - uint8_t maxMaxTransformHierarchyDepthIntra - uint8_t maxDiffCuQpDeltaDepth - uint8_t minMaxNumMergeCand - uint8_t maxMaxNumMergeCand + uint32_t maxPPictureL0ReferenceCount + uint32_t maxBPictureL0ReferenceCount + uint32_t maxL1ReferenceCount + uint32_t maxSubLayerCount + VkBool32 expectDyadicTemporalSubLayerPattern + int32_t minQp + int32_t maxQp + VkBool32 prefersGopRemainingFrames + VkBool32 requiresGopRemainingFrames + VkVideoEncodeH265StdFlagsEXT stdSyntaxFlags + + + VkStructureType sType + void* pNext + VkVideoEncodeH265RateControlFlagsEXT preferredRateControlFlags + uint32_t preferredGopFrameCount + uint32_t preferredIdrPeriod + uint32_t preferredConsecutiveBFrameCount + uint32_t preferredSubLayerCount + VkVideoEncodeH265QpEXT preferredConstantQp + uint32_t preferredMaxL0ReferenceCount + uint32_t preferredMaxL1ReferenceCount #include "vk_video/vulkan_video_codec_h265std_encode.h" - + + + VkStructureType sType + const void* pNext + VkBool32 useMaxLevelIdc + StdVideoH265LevelIdc maxLevelIdc + VkStructureType sType const void* pNext @@ -6537,39 +6973,44 @@ typedef void* MTLSharedEvent_id; uint32_t maxStdPPSCount const VkVideoEncodeH265SessionParametersAddInfoEXT* pParametersAddInfo - - VkStructureType sType + + VkStructureType sType + const void* pNext + VkBool32 writeStdVPS + VkBool32 writeStdSPS + VkBool32 writeStdPPS + uint32_t stdVPSId + uint32_t stdSPSId + uint32_t stdPPSId + + + VkStructureType sType + void* pNext + VkBool32 hasStdVPSOverrides + VkBool32 hasStdSPSOverrides + VkBool32 hasStdPPSOverrides + + + VkStructureType sType const void* pNext - const VkVideoEncodeH265ReferenceListsInfoEXT* pReferenceFinalLists uint32_t naluSliceSegmentEntryCount const VkVideoEncodeH265NaluSliceSegmentInfoEXT* pNaluSliceSegmentEntries - const StdVideoEncodeH265PictureInfo* pCurrentPictureInfo - - - VkStructureType sType - const void* pNext - uint8_t vpsId - uint8_t spsId - VkBool32 emitVpsEnable - VkBool32 emitSpsEnable - uint32_t ppsIdEntryCount - const uint8_t* ppsIdEntries + const StdVideoEncodeH265PictureInfo* pStdPictureInfo VkStructureType sType const void* pNext - uint32_t ctbCount - const VkVideoEncodeH265ReferenceListsInfoEXT* pReferenceFinalLists - const StdVideoEncodeH265SliceSegmentHeader* pSliceSegmentHeaderStd + int32_t constantQp + const StdVideoEncodeH265SliceSegmentHeader* pStdSliceSegmentHeader - + VkStructureType sType const void* pNext + VkVideoEncodeH265RateControlFlagsEXT flags uint32_t gopFrameCount uint32_t idrPeriod uint32_t consecutiveBFrameCount - VkVideoEncodeH265RateControlStructureEXT rateControlStructure - uint8_t subLayerCount + uint32_t subLayerCount int32_t qpI @@ -6581,12 +7022,17 @@ typedef void* MTLSharedEvent_id; uint32_t framePSize uint32_t frameBSize - + + VkStructureType sType + const void* pNext + VkBool32 useGopRemainingFrames + uint32_t gopRemainingI + uint32_t gopRemainingP + uint32_t gopRemainingB + + VkStructureType sType const void* pNext - uint8_t temporalId - VkBool32 useInitialRcQp - VkVideoEncodeH265QpEXT initialRcQp VkBool32 useMinQp VkVideoEncodeH265QpEXT minQp VkBool32 useMaxQp @@ -6599,21 +7045,11 @@ typedef void* MTLSharedEvent_id; const void* pNext StdVideoH265ProfileIdc stdProfileIdc - + VkStructureType sType const void* pNext - int8_t slotIndex const StdVideoEncodeH265ReferenceInfo* pStdReferenceInfo - - VkStructureType sType - const void* pNext - uint8_t referenceList0EntryCount - const VkVideoEncodeH265DpbSlotInfoEXT* pReferenceList0Entries - uint8_t referenceList1EntryCount - const VkVideoEncodeH265DpbSlotInfoEXT* pReferenceList1Entries - const StdVideoEncodeH265ReferenceModifications* pReferenceModifications - VkStructureType sType void* pNext @@ -6754,7 +7190,7 @@ typedef void* MTLSharedEvent_id; const VkDescriptorAddressInfoEXT* pStorageTexelBuffer const VkDescriptorAddressInfoEXT* pUniformBuffer const VkDescriptorAddressInfoEXT* pStorageBuffer - VkDeviceAddress accelerationStructure + VkDeviceAddress accelerationStructure VkStructureType sType @@ -7100,7 +7536,8 @@ typedef void* MTLSharedEvent_id; const void* pNext VkRenderingFlags flags uint32_t viewMask - uint32_t colorAttachmentCount + uint32_t colorAttachmentCount + uint32_t colorAttachmentCount const VkFormat* pColorAttachmentFormats VkFormat depthAttachmentFormat VkFormat stencilAttachmentFormat @@ -7351,7 +7788,42 @@ typedef void* MTLSharedEvent_id; uint32_t usageCountsCount const VkMicromapUsageEXT* pUsageCounts const VkMicromapUsageEXT* const* ppUsageCounts - VkMicromapEXT micromap + VkMicromapEXT micromap + + + VkStructureType sType + void* pNext + VkBool32 displacementMicromap + + + VkStructureType sType + void* pNext + uint32_t maxDisplacementMicromapSubdivisionLevel + + + VkStructureType sType + void* pNext + + VkFormat displacementBiasAndScaleFormat + VkFormat displacementVectorFormat + + VkDeviceOrHostAddressConstKHR displacementBiasAndScaleBuffer + VkDeviceSize displacementBiasAndScaleStride + VkDeviceOrHostAddressConstKHR displacementVectorBuffer + VkDeviceSize displacementVectorStride + VkDeviceOrHostAddressConstKHR displacedMicromapPrimitiveFlags + VkDeviceSize displacedMicromapPrimitiveFlagsStride + VkIndexType indexType + VkDeviceOrHostAddressConstKHR indexBuffer + VkDeviceSize indexStride + + uint32_t baseTriangle + + uint32_t usageCountsCount + const VkMicromapUsageEXT* pUsageCounts + const VkMicromapUsageEXT* const* ppUsageCounts + + VkMicromapEXT micromap VkStructureType sType @@ -7368,6 +7840,11 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 shaderEarlyAndLateFragmentTests + + VkStructureType sType + const void* pNext + VkBool32 acquireUnmodifiedMemory + VkStructureType sType const void* pNext @@ -7630,6 +8107,26 @@ typedef void* MTLSharedEvent_id; uint32_t applicationNameOffset uint32_t applicationVersion uint32_t engineNameOffset + uint32_t engineVersion + uint32_t apiVersion + + + VkStructureType sType + void* pNext + VkBool32 pipelineLibraryGroupHandles + + + VkStructureType sType + const void* pNext + float depthBiasConstantFactor + float depthBiasClamp + float depthBiasSlopeFactor + + + VkStructureType sType + const void* pNext + VkDepthBiasRepresentationEXT depthBiasRepresentation + VkBool32 depthBiasExact VkDeviceAddress srcAddress @@ -7650,7 +8147,77 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 shaderCoreBuiltins - + + VkStructureType sType + void* pNext + VkBool32 dynamicRenderingUnusedAttachments + + + VkStructureType sType + void* pNext + VkPresentModeKHR presentMode + + + VkStructureType sType + void* pNext + VkPresentScalingFlagsEXT supportedPresentScaling + VkPresentGravityFlagsEXT supportedPresentGravityX + VkPresentGravityFlagsEXT supportedPresentGravityY + VkExtent2D minScaledImageExtentSupported minimum image width and height for the surface when scaling is used + VkExtent2D maxScaledImageExtentSupported maximum image width and height for the surface when scaling is used + + + VkStructureType sType + void* pNext + uint32_t presentModeCount + VkPresentModeKHR* pPresentModesOutput list of present modes compatible with the one specified in VkSurfacePresentModeEXT + + + VkStructureType sType + void* pNext + VkBool32 swapchainMaintenance1 + + + VkStructureType sType + const void* pNext + uint32_t swapchainCountCopy of VkPresentInfoKHR::swapchainCount + const VkFence* pFencesFence to signal for each swapchain + + + VkStructureType sType + const void* pNext + uint32_t presentModeCountLength of the pPresentModes array + const VkPresentModeKHR* pPresentModesPresentation modes which will be usable with this swapchain + + + VkStructureType sType + const void* pNext + uint32_t swapchainCountCopy of VkPresentInfoKHR::swapchainCount + const VkPresentModeKHR* pPresentModesPresentation mode for each swapchain + + + VkStructureType sType + const void* pNext + VkPresentScalingFlagsEXT scalingBehavior + VkPresentGravityFlagsEXT presentGravityX + VkPresentGravityFlagsEXT presentGravityY + + + VkStructureType sType + const void* pNext + VkSwapchainKHR swapchainSwapchain for which images are being released + uint32_t imageIndexCountNumber of indices to release + const uint32_t* pImageIndicesIndices of which presentable images to release + + + VkStructureType sType + void* pNext + VkBool32 depthBiasControl + VkBool32 leastRepresentableValueForceUnormRepresentation + VkBool32 floatRepresentation + VkBool32 depthBiasExact + + VkStructureType sType void* pNext VkBool32 rayTracingInvocationReorder @@ -7660,6 +8227,141 @@ typedef void* MTLSharedEvent_id; void* pNext VkRayTracingInvocationReorderModeNV rayTracingInvocationReorderReorderingHint + + VkStructureType sType + void* pNext + VkDirectDriverLoadingFlagsLUNARG flags + PFN_vkGetInstanceProcAddrLUNARG pfnGetInstanceProcAddr + + + VkStructureType sType + void* pNext + VkDirectDriverLoadingModeLUNARG mode + uint32_t driverCount + const VkDirectDriverLoadingInfoLUNARG* pDrivers + + + VkStructureType sType + void* pNext + VkBool32 multiviewPerViewViewports + + + VkStructureType sType + void* pNext + VkBool32 rayTracingPositionFetch + + + VkStructureType sType + void* pNext + uint32_t pixelRate + uint32_t texelRate + uint32_t fmaRate + + + VkStructureType sType + void* pNext + VkBool32 multiviewPerViewRenderAreas + + + VkStructureType sType + const void* pNext + uint32_t perViewRenderAreaCount + const VkRect2D* pPerViewRenderAreas + + + VkStructureType sType + const void* pNext + void* pQueriedLowLatencyData + + + VkStructureType sType + const void* pNext + VkMemoryMapFlags flags + VkDeviceMemory memory + VkDeviceSize offset + VkDeviceSize size + + + VkStructureType sType + const void* pNext + VkMemoryUnmapFlagsKHR flags + VkDeviceMemory memory + + + VkStructureType sType + void* pNext + VkBool32 shaderObject + + + VkStructureType sType + void* pNext + uint8_t shaderBinaryUUID[VK_UUID_SIZE] + uint32_t shaderBinaryVersion + + + VkStructureType sType + const void* pNext + VkShaderCreateFlagsEXT flags + VkShaderStageFlagBits stage + VkShaderStageFlags nextStage + VkShaderCodeTypeEXT codeType + size_t codeSize + const void* pCode + const char* pName + uint32_t setLayoutCount + const VkDescriptorSetLayout* pSetLayouts + uint32_t pushConstantRangeCount + const VkPushConstantRange* pPushConstantRanges + const VkSpecializationInfo* pSpecializationInfo + + + VkStructureType sType + void* pNext + VkBool32 shaderTileImageColorReadAccess + VkBool32 shaderTileImageDepthReadAccess + VkBool32 shaderTileImageStencilReadAccess + + + VkStructureType sType + void* pNext + VkBool32 shaderTileImageCoherentReadAccelerated + VkBool32 shaderTileImageReadSampleFromPixelRateInvocation + VkBool32 shaderTileImageReadFromHelperInvocation + + + VkStructureType sType + const void* pNext + struct _screen_buffer* buffer + + + VkStructureType sType + void* pNext + VkDeviceSize allocationSize + uint32_t memoryTypeBits + + + VkStructureType sType + void* pNext + VkFormat format + uint64_t externalFormat + uint64_t screenUsage + VkFormatFeatureFlags formatFeatures + VkComponentMapping samplerYcbcrConversionComponents + VkSamplerYcbcrModelConversion suggestedYcbcrModel + VkSamplerYcbcrRange suggestedYcbcrRange + VkChromaLocation suggestedXChromaOffset + VkChromaLocation suggestedYChromaOffset + + + VkStructureType sType + void* pNext + uint64_t externalFormat + + + VkStructureType sType + void* pNext + VkBool32 screenBufferImport + @@ -7677,6 +8379,7 @@ typedef void* MTLSharedEvent_id; + @@ -8242,6 +8945,10 @@ typedef void* MTLSharedEvent_id; + + + + Flags @@ -8455,7 +9162,7 @@ typedef void* MTLSharedEvent_id; - + @@ -8480,7 +9187,7 @@ typedef void* MTLSharedEvent_id; - + @@ -8551,7 +9258,7 @@ typedef void* MTLSharedEvent_id; - + NVX_device_generated_commands formerly used these enum values, but that extension has been removed @@ -8559,7 +9266,7 @@ typedef void* MTLSharedEvent_id; value 32 / name VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - + @@ -8680,7 +9387,7 @@ typedef void* MTLSharedEvent_id; - + @@ -8819,7 +9526,8 @@ typedef void* MTLSharedEvent_id; - + + Driver IDs are now represented as enums instead of the old @@ -8848,6 +9556,8 @@ typedef void* MTLSharedEvent_id; + + @@ -8995,9 +9705,9 @@ typedef void* MTLSharedEvent_id; - - - + + + @@ -9025,14 +9735,16 @@ typedef void* MTLSharedEvent_id; - + - + + + @@ -9075,6 +9787,24 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + @@ -9087,6 +9817,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -9236,10 +9969,22 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + @@ -9262,6 +10007,16 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + @@ -9286,10 +10041,10 @@ typedef void* MTLSharedEvent_id; - - - - + + + + @@ -9332,52 +10087,54 @@ typedef void* MTLSharedEvent_id; + + + + + - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -9453,47 +10210,44 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -9633,6 +10387,11 @@ typedef void* MTLSharedEvent_id; + + + + + @@ -9645,6 +10404,19 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + VkResult vkCreateInstance @@ -9713,7 +10485,14 @@ typedef void* MTLSharedEvent_id; VkImageCreateFlags flags VkImageFormatProperties* pImageFormatProperties - + + VkResult vkCreateDevice + VkPhysicalDevice physicalDevice + const VkDeviceCreateInfo* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkDevice* pDevice + + VkResult vkCreateDevice VkPhysicalDevice physicalDevice const VkDeviceCreateInfo* pCreateInfo @@ -9743,12 +10522,19 @@ typedef void* MTLSharedEvent_id; uint32_t* pPropertyCount VkExtensionProperties* pProperties - + + VkResult vkEnumerateDeviceLayerProperties + VkPhysicalDevice physicalDevice + uint32_t* pPropertyCount + VkLayerProperties* pProperties + + VkResult vkEnumerateDeviceLayerProperties VkPhysicalDevice physicalDevice uint32_t* pPropertyCount VkLayerProperties* pProperties + VkResult vkEnumerateDeviceExtensionProperties VkPhysicalDevice physicalDevice @@ -10054,7 +10840,14 @@ typedef void* MTLSharedEvent_id; VkShaderModule shaderModule const VkAllocationCallbacks* pAllocator - + + VkResult vkCreatePipelineCache + VkDevice device + const VkPipelineCacheCreateInfo* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkPipelineCache* pPipelineCache + + VkResult vkCreatePipelineCache VkDevice device const VkPipelineCacheCreateInfo* pCreateInfo @@ -10081,7 +10874,7 @@ typedef void* MTLSharedEvent_id; uint32_t srcCacheCount const VkPipelineCache* pSrcCaches - + VkResult vkCreateGraphicsPipelines VkDevice device VkPipelineCache pipelineCache @@ -10090,12 +10883,30 @@ typedef void* MTLSharedEvent_id; const VkAllocationCallbacks* pAllocator VkPipeline* pPipelines - - VkResult vkCreateComputePipelines + + VkResult vkCreateGraphicsPipelines VkDevice device - VkPipelineCache pipelineCache + VkPipelineCache pipelineCache uint32_t createInfoCount - const VkComputePipelineCreateInfo* pCreateInfos + const VkGraphicsPipelineCreateInfo* pCreateInfos + const VkAllocationCallbacks* pAllocator + VkPipeline* pPipelines + + + VkResult vkCreateComputePipelines + VkDevice device + VkPipelineCache pipelineCache + uint32_t createInfoCount + const VkComputePipelineCreateInfo* pCreateInfos + const VkAllocationCallbacks* pAllocator + VkPipeline* pPipelines + + + VkResult vkCreateComputePipelines + VkDevice device + VkPipelineCache pipelineCache + uint32_t createInfoCount + const VkComputePipelineCreateInfo* pCreateInfos const VkAllocationCallbacks* pAllocator VkPipeline* pPipelines @@ -10265,7 +11076,7 @@ typedef void* MTLSharedEvent_id; the sname:VkCommandPool that pname:commandBuffer was allocated from - + VkResult vkEndCommandBuffer VkCommandBuffer commandBuffer @@ -10286,6 +11097,11 @@ typedef void* MTLSharedEvent_id; VkPipelineBindPoint pipelineBindPoint VkPipeline pipeline + + void vkCmdSetAttachmentFeedbackLoopEnableEXT + VkCommandBuffer commandBuffer + VkImageAspectFlags aspectMask + void vkCmdSetViewport VkCommandBuffer commandBuffer @@ -10436,6 +11252,19 @@ typedef void* MTLSharedEvent_id; void vkCmdSubpassShadingHUAWEI VkCommandBuffer commandBuffer + + void vkCmdDrawClusterHUAWEI + VkCommandBuffer commandBuffer + uint32_t groupCountX + uint32_t groupCountY + uint32_t groupCountZ + + + void vkCmdDrawClusterIndirectHUAWEI + VkCommandBuffer commandBuffer + VkBuffer buffer + VkDeviceSize offset + void vkCmdCopyBuffer VkCommandBuffer commandBuffer @@ -10613,14 +11442,14 @@ typedef void* MTLSharedEvent_id; void vkCmdEndConditionalRenderingEXT VkCommandBuffer commandBuffer - + void vkCmdResetQueryPool VkCommandBuffer commandBuffer VkQueryPool queryPool uint32_t firstQuery uint32_t queryCount - + void vkCmdWriteTimestamp VkCommandBuffer commandBuffer VkPipelineStageFlagBits pipelineStage @@ -10727,7 +11556,8 @@ typedef void* MTLSharedEvent_id; VkResult vkCreateSharedSwapchainsKHR VkDevice device uint32_t swapchainCount - const VkSwapchainCreateInfoKHR* pCreateInfos + const VkSwapchainCreateInfoKHR* pCreateInfos + const VkSwapchainCreateInfoKHR* pCreateInfos const VkAllocationCallbacks* pAllocator VkSwapchainKHR* pSwapchains @@ -10767,7 +11597,8 @@ typedef void* MTLSharedEvent_id; VkResult vkCreateSwapchainKHR VkDevice device - const VkSwapchainCreateInfoKHR* pCreateInfo + const VkSwapchainCreateInfoKHR* pCreateInfo + const VkSwapchainCreateInfoKHR* pCreateInfo const VkAllocationCallbacks* pAllocator VkSwapchainKHR* pSwapchain @@ -11116,6 +11947,24 @@ typedef void* MTLSharedEvent_id; const VkMemoryGetRemoteAddressInfoNV* pMemoryGetRemoteAddressInfo VkRemoteAddressNV* pAddress + + VkResult vkGetMemorySciBufNV + VkDevice device + const VkMemoryGetSciBufInfoNV* pGetSciBufInfo + NvSciBufObj* pHandle + + + VkResult vkGetPhysicalDeviceExternalMemorySciBufPropertiesNV + VkPhysicalDevice physicalDevice + VkExternalMemoryHandleTypeFlagBits handleType + NvSciBufObj handle + VkMemorySciBufPropertiesNV* pMemorySciBufProperties + + + VkResult vkGetPhysicalDeviceSciBufAttributesNV + VkPhysicalDevice physicalDevice + NvSciBufAttrList pAttributes + void vkGetPhysicalDeviceExternalSemaphoreProperties VkPhysicalDevice physicalDevice @@ -11185,6 +12034,58 @@ typedef void* MTLSharedEvent_id; VkDevice device const VkImportFenceFdInfoKHR* pImportFenceFdInfo + + VkResult vkGetFenceSciSyncFenceNV + VkDevice device + const VkFenceGetSciSyncInfoNV* pGetSciSyncHandleInfo + void* pHandle + + + VkResult vkGetFenceSciSyncObjNV + VkDevice device + const VkFenceGetSciSyncInfoNV* pGetSciSyncHandleInfo + void* pHandle + + + VkResult vkImportFenceSciSyncFenceNV + VkDevice device + const VkImportFenceSciSyncInfoNV* pImportFenceSciSyncInfo + + + VkResult vkImportFenceSciSyncObjNV + VkDevice device + const VkImportFenceSciSyncInfoNV* pImportFenceSciSyncInfo + + + VkResult vkGetSemaphoreSciSyncObjNV + VkDevice device + const VkSemaphoreGetSciSyncInfoNV* pGetSciSyncInfo + void* pHandle + + + VkResult vkImportSemaphoreSciSyncObjNV + VkDevice device + const VkImportSemaphoreSciSyncInfoNV* pImportSemaphoreSciSyncInfo + + + VkResult vkGetPhysicalDeviceSciSyncAttributesNV + VkPhysicalDevice physicalDevice + const VkSciSyncAttributesInfoNV* pSciSyncAttributesInfo + NvSciSyncAttrList pAttributes + + + VkResult vkCreateSemaphoreSciSyncPoolNV + VkDevice device + const VkSemaphoreSciSyncPoolCreateInfoNV* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkSemaphoreSciSyncPoolNV* pSemaphorePool + + + void vkDestroySemaphoreSciSyncPoolNV + VkDevice device + VkSemaphoreSciSyncPoolNV semaphorePool + const VkAllocationCallbacks* pAllocator + VkResult vkReleaseDisplayEXT VkPhysicalDevice physicalDevice @@ -11410,6 +12311,16 @@ typedef void* MTLSharedEvent_id; uint32_t discardRectangleCount const VkRect2D* pDiscardRectangles + + void vkCmdSetDiscardRectangleEnableEXT + VkCommandBuffer commandBuffer + VkBool32 discardRectangleEnable + + + void vkCmdSetDiscardRectangleModeEXT + VkCommandBuffer commandBuffer + VkDiscardRectangleModeEXT discardRectangleMode + void vkCmdSetSampleLocationsEXT VkCommandBuffer commandBuffer @@ -11844,6 +12755,13 @@ typedef void* MTLSharedEvent_id; uint32_t exclusiveScissorCount const VkRect2D* pExclusiveScissors + + void vkCmdSetExclusiveScissorEnableNV + VkCommandBuffer commandBuffer + uint32_t firstExclusiveScissor + uint32_t exclusiveScissorCount + const VkBool32* pExclusiveScissorEnables + void vkCmdBindShadingRateImageNV VkCommandBuffer commandBuffer @@ -12091,7 +13009,7 @@ typedef void* MTLSharedEvent_id; size_t dataSize void* pData - + VkResult vkCreateRayTracingPipelinesNV VkDevice device VkPipelineCache pipelineCache @@ -12100,7 +13018,16 @@ typedef void* MTLSharedEvent_id; const VkAllocationCallbacks* pAllocator VkPipeline* pPipelines - + + VkResult vkCreateRayTracingPipelinesNV + VkDevice device + VkPipelineCache pipelineCache + uint32_t createInfoCount + const VkRayTracingPipelineCreateInfoNV* pCreateInfos + const VkAllocationCallbacks* pAllocator + VkPipeline* pPipelines + + VkResult vkCreateRayTracingPipelinesKHR VkDevice device VkDeferredOperationKHR deferredOperation @@ -12110,6 +13037,16 @@ typedef void* MTLSharedEvent_id; const VkAllocationCallbacks* pAllocator VkPipeline* pPipelines + + VkResult vkCreateRayTracingPipelinesKHR + VkDevice device + VkDeferredOperationKHR deferredOperation + VkPipelineCache pipelineCache + uint32_t createInfoCount + const VkRayTracingPipelineCreateInfoKHR* pCreateInfos + const VkAllocationCallbacks* pAllocator + VkPipeline* pPipelines + VkResult vkGetPhysicalDeviceCooperativeMatrixPropertiesNV VkPhysicalDevice physicalDevice @@ -12316,6 +13253,14 @@ typedef void* MTLSharedEvent_id; uint32_t lineStippleFactor uint16_t lineStipplePattern + + VkResult vkGetFaultData + VkDevice device + VkFaultQueryBehavior faultQueryBehavior + VkBool32* pUnrecordedFaults + uint32_t* pFaultCount + VkFaultData* pFaults + VkResult vkGetPhysicalDeviceToolProperties VkPhysicalDevice physicalDevice @@ -12733,6 +13678,17 @@ typedef void* MTLSharedEvent_id; const VkResolveImageInfo2* pResolveImageInfo + + void vkCmdRefreshObjectsKHR + VkCommandBuffer commandBuffer + const VkRefreshObjectListKHR* pRefreshObjects + + + VkResult vkGetPhysicalDeviceRefreshableObjectTypesKHR + VkPhysicalDevice physicalDevice + uint32_t* pRefreshableObjectTypeCount + VkObjectType* pRefreshableObjectTypes + void vkCmdSetFragmentShadingRateKHR VkCommandBuffer commandBuffer @@ -12831,6 +13787,13 @@ typedef void* MTLSharedEvent_id; uint32_t* pCheckpointDataCount VkCheckpointData2NV* pCheckpointData + + void vkGetCommandPoolMemoryConsumption + VkDevice device + VkCommandPool commandPool + VkCommandBuffer commandBuffer + VkCommandPoolMemoryConsumption* pConsumption + VkResult vkGetPhysicalDeviceVideoCapabilitiesKHR VkPhysicalDevice physicalDevice @@ -12844,7 +13807,13 @@ typedef void* MTLSharedEvent_id; uint32_t* pVideoFormatPropertyCount VkVideoFormatPropertiesKHR* pVideoFormatProperties - + + VkResult vkGetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR + VkPhysicalDevice physicalDevice + const VkPhysicalDeviceVideoEncodeQualityLevelInfoKHR* pQualityLevelInfo + VkVideoEncodeQualityLevelPropertiesKHR* pQualityLevelProperties + + VkResult vkCreateVideoSessionKHR VkDevice device const VkVideoSessionCreateInfoKHR* pCreateInfo @@ -12857,33 +13826,41 @@ typedef void* MTLSharedEvent_id; VkVideoSessionKHR videoSession const VkAllocationCallbacks* pAllocator - + VkResult vkCreateVideoSessionParametersKHR VkDevice device const VkVideoSessionParametersCreateInfoKHR* pCreateInfo const VkAllocationCallbacks* pAllocator VkVideoSessionParametersKHR* pVideoSessionParameters - + VkResult vkUpdateVideoSessionParametersKHR VkDevice device VkVideoSessionParametersKHR videoSessionParameters const VkVideoSessionParametersUpdateInfoKHR* pUpdateInfo + + VkResult vkGetEncodedVideoSessionParametersKHR + VkDevice device + const VkVideoEncodeSessionParametersGetInfoKHR* pVideoSessionParametersInfo + VkVideoEncodeSessionParametersFeedbackInfoKHR* pFeedbackInfo + size_t* pDataSize + void* pData + void vkDestroyVideoSessionParametersKHR VkDevice device VkVideoSessionParametersKHR videoSessionParameters const VkAllocationCallbacks* pAllocator - + VkResult vkGetVideoSessionMemoryRequirementsKHR VkDevice device VkVideoSessionKHR videoSession uint32_t* pMemoryRequirementsCount VkVideoSessionMemoryRequirementsKHR* pMemoryRequirements - + VkResult vkBindVideoSessionMemoryKHR VkDevice device VkVideoSessionKHR videoSession @@ -13287,9 +14264,64 @@ typedef void* MTLSharedEvent_id; VkDeviceFaultCountsEXT* pFaultCounts VkDeviceFaultInfoEXT* pFaultInfo + + void vkCmdSetDepthBias2EXT + VkCommandBuffer commandBuffer + const VkDepthBiasInfoEXT* pDepthBiasInfo + + + VkResult vkReleaseSwapchainImagesEXT + VkDevice device + const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo + + + VkResult vkMapMemory2KHR + VkDevice device + const VkMemoryMapInfoKHR* pMemoryMapInfo + void** ppData + + + VkResult vkUnmapMemory2KHR + VkDevice device + const VkMemoryUnmapInfoKHR* pMemoryUnmapInfo + + + VkResult vkCreateShadersEXT + VkDevice device + uint32_t createInfoCount + const VkShaderCreateInfoEXT* pCreateInfos + const VkAllocationCallbacks* pAllocator + VkShaderEXT* pShaders + + + void vkDestroyShaderEXT + VkDevice device + VkShaderEXT shader + const VkAllocationCallbacks* pAllocator + + + VkResult vkGetShaderBinaryDataEXT + VkDevice device + VkShaderEXT shader + size_t* pDataSize + void* pData + + + void vkCmdBindShadersEXT + VkCommandBuffer commandBuffer + uint32_t stageCount + const VkShaderStageFlagBits* pStages + const VkShaderEXT* pShaders + + + VkResult vkGetScreenBufferPropertiesQNX + VkDevice device + const struct _screen_buffer* buffer + VkScreenBufferPropertiesQNX* pProperties + - + @@ -13803,7 +14835,7 @@ typedef void* MTLSharedEvent_id; - + @@ -13957,7 +14989,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14130,12 +15162,12 @@ typedef void* MTLSharedEvent_id; - + - + @@ -14330,7 +15362,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14595,8 +15627,142 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -14618,7 +15784,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14639,7 +15805,7 @@ typedef void* MTLSharedEvent_id; - + This duplicates definitions in VK_KHR_device_group below @@ -14663,7 +15829,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14694,7 +15860,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14704,7 +15870,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14715,7 +15881,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14726,7 +15892,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14737,13 +15903,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -14754,7 +15920,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14792,8 +15958,9 @@ typedef void* MTLSharedEvent_id; - - + + + @@ -14805,7 +15972,7 @@ typedef void* MTLSharedEvent_id; - + This duplicates definitions in other extensions, below @@ -14818,18 +15985,18 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -14879,7 +16046,7 @@ typedef void* MTLSharedEvent_id; - + @@ -14897,40 +16064,40 @@ typedef void* MTLSharedEvent_id; - + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + - - + + - - - - - - + + + + + + @@ -14986,28 +16153,28 @@ typedef void* MTLSharedEvent_id; - + - + - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -15022,9 +16189,9 @@ typedef void* MTLSharedEvent_id; - - - + + + @@ -15051,7 +16218,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15163,111 +16330,120 @@ typedef void* MTLSharedEvent_id; - + - + - + - + - + + + + - - - - + + + + - - - + + + - + + + - + - + - + - + - + + + + - - - - - + + + + - - + + + - - + + + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + @@ -15291,7 +16467,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15311,27 +16487,27 @@ typedef void* MTLSharedEvent_id; - + - + - + - + - + - + - + @@ -15360,7 +16536,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15370,7 +16546,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15379,10 +16555,11 @@ typedef void* MTLSharedEvent_id; - + - - + + + @@ -15391,7 +16568,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15430,7 +16607,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15440,7 +16617,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15451,7 +16628,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15459,7 +16636,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15490,7 +16667,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15520,14 +16697,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -15536,7 +16713,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15560,7 +16737,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15570,7 +16747,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15588,7 +16765,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15610,7 +16787,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15620,7 +16797,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15634,12 +16811,12 @@ typedef void* MTLSharedEvent_id; - + - - + + @@ -15648,7 +16825,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15661,7 +16838,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15694,7 +16871,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15708,7 +16885,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15724,7 +16901,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15738,7 +16915,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15746,7 +16923,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15771,7 +16948,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15782,7 +16959,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15798,7 +16975,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15810,7 +16987,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15819,16 +16996,16 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -15847,7 +17024,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15857,7 +17034,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15865,7 +17042,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15875,7 +17052,7 @@ typedef void* MTLSharedEvent_id; - + @@ -15891,11 +17068,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -15916,14 +17093,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -15931,19 +17108,19 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -15964,7 +17141,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16004,11 +17181,11 @@ typedef void* MTLSharedEvent_id; - - + + - + @@ -16029,18 +17206,22 @@ typedef void* MTLSharedEvent_id; - + - + + + + + @@ -16049,7 +17230,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16061,7 +17242,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16076,9 +17257,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -16096,10 +17278,10 @@ typedef void* MTLSharedEvent_id; - + - + @@ -16121,7 +17303,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16136,7 +17318,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16166,7 +17348,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16178,7 +17360,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16202,7 +17384,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16213,7 +17395,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16227,7 +17409,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16239,7 +17421,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16271,13 +17453,17 @@ typedef void* MTLSharedEvent_id; + + + + - + - - + + @@ -16305,7 +17491,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16319,7 +17505,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16329,7 +17515,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16349,7 +17535,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16359,7 +17545,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16375,21 +17561,21 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -16399,7 +17585,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16435,7 +17621,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16456,12 +17642,12 @@ typedef void* MTLSharedEvent_id; - + - + @@ -16476,7 +17662,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16519,7 +17705,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16540,7 +17726,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16558,7 +17744,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16581,7 +17767,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16593,7 +17779,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16612,7 +17798,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16620,7 +17806,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16689,7 +17875,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16774,11 +17960,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -16823,7 +18009,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16854,7 +18040,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16864,13 +18050,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -16949,11 +18135,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -16966,7 +18152,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16989,7 +18175,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17019,7 +18205,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17050,7 +18236,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17060,7 +18246,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17088,7 +18274,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17184,7 +18370,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17200,12 +18386,12 @@ typedef void* MTLSharedEvent_id; - + - - + + @@ -17213,7 +18399,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17221,7 +18407,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17256,17 +18442,17 @@ typedef void* MTLSharedEvent_id; - + - - + + - + @@ -17280,7 +18466,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17288,7 +18474,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17310,7 +18496,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17318,7 +18504,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17342,7 +18528,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17353,7 +18539,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17367,28 +18553,28 @@ typedef void* MTLSharedEvent_id; - + - - - - - - - - - + + + + + + + + + - - + + - - - - + + + + - + @@ -17412,7 +18598,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17425,7 +18611,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17460,11 +18646,10 @@ typedef void* MTLSharedEvent_id; - + - + @@ -17488,7 +18673,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17500,14 +18685,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -17524,14 +18709,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -17539,7 +18724,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17557,7 +18742,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17565,7 +18750,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17573,19 +18758,21 @@ typedef void* MTLSharedEvent_id; - + - + - + + + - + @@ -17597,7 +18784,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17630,7 +18817,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17643,7 +18830,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17677,7 +18864,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17685,7 +18872,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17693,7 +18880,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17705,7 +18892,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17715,7 +18902,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17729,7 +18916,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17740,7 +18927,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17760,7 +18947,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17777,7 +18964,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17795,8 +18982,8 @@ typedef void* MTLSharedEvent_id; - - + + @@ -17805,7 +18992,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17819,7 +19006,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17843,11 +19030,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -17863,7 +19050,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17897,7 +19084,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17911,13 +19098,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -17925,7 +19112,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17935,7 +19122,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17943,7 +19130,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17951,7 +19138,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17980,7 +19167,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18008,13 +19195,13 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -18027,7 +19214,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18037,7 +19224,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18046,7 +19233,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18061,7 +19248,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18076,7 +19263,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18084,7 +19271,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18092,7 +19279,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18100,7 +19287,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18113,7 +19300,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18127,18 +19314,18 @@ typedef void* MTLSharedEvent_id; - + - + - + - + @@ -18148,7 +19335,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18182,7 +19369,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18197,7 +19384,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18205,7 +19392,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18232,7 +19419,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18247,7 +19434,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18279,7 +19466,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18296,7 +19483,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18321,26 +19508,34 @@ typedef void* MTLSharedEvent_id; - + + - + - - + + + + + + + + + - + - + @@ -18348,19 +19543,43 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -18368,7 +19587,7 @@ typedef void* MTLSharedEvent_id; - + This extension requires buffer_device_address functionality. @@ -18416,7 +19635,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18432,7 +19651,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18442,7 +19661,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18452,7 +19671,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18463,13 +19682,21 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + - + @@ -18484,7 +19711,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18492,7 +19719,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18502,7 +19729,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18563,7 +19790,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18578,7 +19805,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18590,13 +19817,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -18606,7 +19833,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18632,7 +19859,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18646,18 +19873,15 @@ typedef void* MTLSharedEvent_id; - - - - - - - + + + + - + - + @@ -18668,9 +19892,15 @@ typedef void* MTLSharedEvent_id; + + + + + + - + @@ -18678,10 +19908,13 @@ typedef void* MTLSharedEvent_id; + - + + + @@ -18690,6 +19923,10 @@ typedef void* MTLSharedEvent_id; + + + + @@ -18703,14 +19940,23 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + - + - + @@ -18765,10 +20011,17 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + @@ -18778,10 +20031,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -18835,7 +20090,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18874,65 +20129,65 @@ typedef void* MTLSharedEvent_id; - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -18943,7 +20198,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18995,7 +20250,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19021,7 +20276,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19039,7 +20294,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19047,7 +20302,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19057,7 +20312,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19071,7 +20326,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19079,7 +20334,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19094,7 +20349,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19117,7 +20372,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19137,7 +20392,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19147,7 +20402,7 @@ typedef void* MTLSharedEvent_id; - + VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT and @@ -19170,7 +20425,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19181,7 +20436,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19189,13 +20444,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -19203,7 +20458,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19211,7 +20466,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19245,7 +20500,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19267,7 +20522,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19280,7 +20535,7 @@ typedef void* MTLSharedEvent_id; - + VkPhysicalDevice4444FormatsFeaturesEXT and @@ -19295,9 +20550,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -19313,7 +20568,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19333,7 +20588,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19341,7 +20596,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19349,7 +20604,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19360,7 +20615,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19372,7 +20627,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19386,7 +20641,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19400,7 +20655,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19410,7 +20665,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19424,7 +20679,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19434,7 +20689,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19460,7 +20715,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19488,7 +20743,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19503,7 +20758,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19516,7 +20771,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19566,7 +20821,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19583,7 +20838,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19595,7 +20850,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19609,7 +20864,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19622,20 +20877,60 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -19644,7 +20939,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19657,7 +20952,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19675,7 +20970,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19698,7 +20993,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19710,7 +21005,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19737,7 +21032,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19746,14 +21041,14 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -19764,12 +21059,12 @@ typedef void* MTLSharedEvent_id; - + - - + + @@ -19787,7 +21082,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19797,7 +21092,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19811,7 +21106,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19820,20 +21115,24 @@ typedef void* MTLSharedEvent_id; - + - + - - + + + + + + - + @@ -19899,13 +21198,20 @@ typedef void* MTLSharedEvent_id; - + - - - - - + + + + + + + + + + + + @@ -19946,12 +21252,19 @@ typedef void* MTLSharedEvent_id; - + - - - - + + + + + + + + + + + @@ -19990,7 +21303,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20000,7 +21313,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20009,7 +21322,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20033,10 +21346,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -20051,11 +21366,15 @@ typedef void* MTLSharedEvent_id; - + - - - + + + + + + + @@ -20065,7 +21384,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20079,7 +21398,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20087,7 +21406,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20108,7 +21427,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20121,7 +21440,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20135,7 +21454,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20162,14 +21481,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -20185,7 +21504,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20197,10 +21516,12 @@ typedef void* MTLSharedEvent_id; - + - - + + + + @@ -20209,7 +21530,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20231,7 +21552,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20247,7 +21568,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20295,7 +21616,6 @@ typedef void* MTLSharedEvent_id; - @@ -20326,21 +21646,27 @@ typedef void* MTLSharedEvent_id; + + + + - + - - + + + + - + - + @@ -20414,19 +21740,19 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -20443,10 +21769,17 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + @@ -20454,6 +21787,7 @@ typedef void* MTLSharedEvent_id; + @@ -20462,7 +21796,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20479,7 +21813,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20495,7 +21829,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20540,7 +21874,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20548,14 +21882,14 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -20575,6 +21909,7 @@ typedef void* MTLSharedEvent_id; + @@ -20649,19 +21984,108 @@ typedef void* MTLSharedEvent_id; - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + @@ -20670,7 +22094,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20683,7 +22107,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20705,19 +22129,54 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -20746,7 +22205,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20772,7 +22231,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20782,16 +22241,20 @@ typedef void* MTLSharedEvent_id; - + - - + + + + - + - - + + + + @@ -20818,6 +22281,267 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22387,11 +24111,14 @@ typedef void* MTLSharedEvent_id; + + + - + @@ -22404,6 +24131,15 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + @@ -22777,7 +24513,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22875,5 +24611,355 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fragment shader stage is added by the VK_EXT_shader_tile_image extension + + + + + + + Fragment shader stage is added by the VK_EXT_shader_tile_image extension + + + + + + + + + + + + + + + + + + + TODO/Suggestion. Introduce 'synclist' (could be a different name) element + that specifies the list of stages, accesses, etc. This list can be used by + 'syncaccess' or 'syncstage' elements. For example, 'syncsupport' in addition to the + 'stage' attribute can support 'list' attribute to reference 'synclist'. + We can have the lists defined for ALL stages and it can be shared between MEMORY_READ + and MEMORY_WRITE accesses. Similarly, ALL shader stages list is often used. This proposal + is a way to fix duplication problem. When new stage is added multiple places needs to be + updated. It is potential source of bugs. The expectation such setup will produce more + robust system and also more simple structure to review and validate. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT + VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT + VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT + VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT + VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT + VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT + VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT + VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT + VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR + VK_PIPELINE_STAGE_2_FRAGMENT_DENSITY_PROCESS_BIT_EXT + VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT + VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT + + + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT + VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_EXT + VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_EXT + VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR + VK_PIPELINE_STAGE_2_FRAGMENT_DENSITY_PROCESS_BIT_EXT + VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT + VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT + + + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT + VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT + + + VK_PIPELINE_STAGE_2_TRANSFER_BIT + + + VK_PIPELINE_STAGE_2_HOST_BIT + + + VK_PIPELINE_STAGE_2_SUBPASS_SHADING_BIT_HUAWEI + + + VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV + + + VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR + + + VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_COPY_BIT_KHR + + + VK_PIPELINE_STAGE_2_MICROMAP_BUILD_BIT_EXT + + + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT + VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR + + + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR + + + VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR + + + VK_PIPELINE_STAGE_2_OPTICAL_FLOW_BIT_NV + + From 5b8e532b686c1c4980313ec15338ba04db4f30e0 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 17:45:01 +0300 Subject: [PATCH 453/758] winemenubuilder: Force-disable creating associations. --- programs/winemenubuilder/winemenubuilder.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index 1579ca8dafa..e62831c71f2 100644 --- a/programs/winemenubuilder/winemenubuilder.c +++ b/programs/winemenubuilder/winemenubuilder.c @@ -2792,20 +2792,7 @@ static BOOL init_xdg(void) static BOOL associations_enabled(void) { - BOOL ret = TRUE; - HKEY hkey; - BYTE buf[32]; - DWORD len; - - if ((hkey = open_associations_reg_key())) - { - len = sizeof(buf); - if (!RegQueryValueExA(hkey, "Enable", NULL, NULL, buf, &len)) - ret = IS_OPTION_TRUE(buf[0]); - RegCloseKey( hkey ); - } - - return ret; + return FALSE; } /*********************************************************************** From 9a29aa8feea69862dba4defd6593e781580a2b59 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 17:46:18 +0300 Subject: [PATCH 454/758] winemenubuilder: Save .desktop files in c:\proton_shortcuts\. --- programs/winemenubuilder/winemenubuilder.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index e62831c71f2..0ed4f7042c6 100644 --- a/programs/winemenubuilder/winemenubuilder.c +++ b/programs/winemenubuilder/winemenubuilder.c @@ -1266,7 +1266,7 @@ static BOOL write_desktop_entry(const WCHAR *link, const WCHAR *location, const char *workdir_unix; int needs_chmod = FALSE; const WCHAR *name; - const WCHAR *prefix = _wgetenv( L"WINECONFIGDIR" ); + WCHAR *shortcuts_dir; WINE_TRACE("(%s,%s,%s,%s,%s,%s,%s,%s,%s)\n", wine_dbgstr_w(link), wine_dbgstr_w(location), wine_dbgstr_w(linkname), wine_dbgstr_w(path), wine_dbgstr_w(args), @@ -1274,11 +1274,12 @@ static BOOL write_desktop_entry(const WCHAR *link, const WCHAR *location, const wine_dbgstr_w(wmclass)); name = PathFindFileNameW( linkname ); - if (!location) - { - location = heap_wprintf(L"%s\\%s.desktop", xdg_desktop_dir, name); - needs_chmod = TRUE; - } + + shortcuts_dir = heap_wprintf(L"%s", L"c:\\proton_shortcuts"); + create_directories(shortcuts_dir); + location = heap_wprintf(L"%s\\%s.desktop", shortcuts_dir, name); + heap_free(shortcuts_dir); + needs_chmod = TRUE; file = _wfopen( location, L"wb" ); if (file == NULL) From 329e3bce7ec286b736a05fbfccd39f6499cf4ef0 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 17:47:02 +0300 Subject: [PATCH 455/758] winemenubuilder: User raw exe path in the created .desktop files. We don't need the magic wine/prefix invocations. --- programs/winemenubuilder/winemenubuilder.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index 0ed4f7042c6..a99c88dcfa6 100644 --- a/programs/winemenubuilder/winemenubuilder.c +++ b/programs/winemenubuilder/winemenubuilder.c @@ -1288,13 +1288,8 @@ static BOOL write_desktop_entry(const WCHAR *link, const WCHAR *location, const fprintf(file, "[Desktop Entry]\n"); fprintf(file, "Name=%s\n", wchars_to_utf8_chars(name)); fprintf(file, "Exec=" ); - if (prefix) - { - char *path = wine_get_unix_file_name( prefix ); - fprintf(file, "env WINEPREFIX=\"%s\" ", path); - heap_free( path ); - } - fprintf(file, "wine %s", escape(path)); + + fprintf(file, "%s", escape(path)); if (args) fprintf(file, " %s", escape(args) ); fputc( '\n', file ); fprintf(file, "Type=Application\n"); From c03d5dfdac46be74c4eae5976b24e3cda5f5cc38 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 17:48:37 +0300 Subject: [PATCH 456/758] winemenubuilder: Don't create menu entries. --- programs/winemenubuilder/winemenubuilder.c | 100 --------------------- 1 file changed, 100 deletions(-) diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index a99c88dcfa6..6f653444b33 100644 --- a/programs/winemenubuilder/winemenubuilder.c +++ b/programs/winemenubuilder/winemenubuilder.c @@ -1321,100 +1321,6 @@ static BOOL write_desktop_entry(const WCHAR *link, const WCHAR *location, const return TRUE; } -static BOOL write_directory_entry(const WCHAR *directory, const WCHAR *location) -{ - FILE *file; - - WINE_TRACE("(%s,%s)\n", wine_dbgstr_w(directory), wine_dbgstr_w(location)); - - file = _wfopen( location, L"wb" ); - if (file == NULL) - return FALSE; - - fprintf(file, "[Desktop Entry]\n"); - fprintf(file, "Type=Directory\n"); - if (wcscmp(directory, L"wine") == 0) - { - fprintf(file, "Name=Wine\n"); - fprintf(file, "Icon=wine\n"); - } - else - { - fprintf(file, "Name=%s\n", wchars_to_utf8_chars(directory)); - fprintf(file, "Icon=folder\n"); - } - - fclose(file); - return TRUE; -} - -static BOOL write_menu_file(const WCHAR *windows_link, const WCHAR *link) -{ - WCHAR tempfilename[MAX_PATH]; - FILE *tempfile = NULL; - WCHAR *filename, *lastEntry, *menuPath; - int i; - int count = 0; - BOOL ret = FALSE; - - WINE_TRACE("(%s)\n", wine_dbgstr_w(link)); - - GetTempFileNameW( xdg_menu_dir, L"mnu", 0, tempfilename ); - if (!(tempfile = _wfopen( tempfilename, L"wb" ))) return FALSE; - - fprintf(tempfile, "\n"); - fprintf(tempfile, "\n"); - fprintf(tempfile, " Applications\n"); - - filename = heap_wprintf(L"wine\\%s.desktop", link); - lastEntry = filename; - for (i = 0; filename[i]; i++) - { - if (filename[i] == '\\') - { - WCHAR *dir_file_name; - const char *prefix = count ? "" : "wine-"; - - filename[i] = 0; - fprintf(tempfile, " \n"); - fprintf(tempfile, " %s%s\n", - prefix, wchars_to_xml_text(filename)); - fprintf(tempfile, " %s%s.directory\n", - prefix, wchars_to_xml_text(filename)); - dir_file_name = heap_wprintf(L"%s\\desktop-directories\\%s%s.directory", - xdg_data_dir, count ? L"" : L"wine-", filename); - if (GetFileAttributesW( dir_file_name ) == INVALID_FILE_ATTRIBUTES) - write_directory_entry(lastEntry, dir_file_name); - heap_free(dir_file_name); - filename[i] = '-'; - lastEntry = &filename[i+1]; - ++count; - } - } - filename[i] = 0; - - fprintf(tempfile, " \n"); - fprintf(tempfile, " %s\n", wchars_to_xml_text(filename)); - fprintf(tempfile, " \n"); - for (i = 0; i < count; i++) - fprintf(tempfile, " \n"); - fprintf(tempfile, "\n"); - - menuPath = heap_wprintf(L"%s\\%s", xdg_menu_dir, filename); - lstrcpyW(menuPath + lstrlenW(menuPath) - lstrlenW(L".desktop"), L".menu"); - - fclose(tempfile); - ret = MoveFileExW( tempfilename, menuPath, MOVEFILE_REPLACE_EXISTING ); - if (ret) - register_menus_entry(menuPath, windows_link); - else - DeleteFileW( tempfilename ); - heap_free(filename); - heap_free(menuPath); - return ret; -} - static BOOL write_menu_entry(const WCHAR *windows_link, const WCHAR *link, const WCHAR *path, const WCHAR *args, const WCHAR *descr, const WCHAR *workdir, const WCHAR *icon, const WCHAR *wmclass) { @@ -1444,12 +1350,6 @@ static BOOL write_menu_entry(const WCHAR *windows_link, const WCHAR *link, const goto end; } - if (!write_menu_file(windows_link, link)) - { - WINE_WARN("couldn't make menu file %s\n", wine_dbgstr_w(filename)); - ret = FALSE; - } - end: heap_free(desktopPath); heap_free(filename); From e8fa9810141d09c1758ea39de73d282030e111ae Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 17:49:13 +0300 Subject: [PATCH 457/758] winemenubuilder: Save icons in c:\proton_shortcuts\icons\ --- programs/winemenubuilder/winemenubuilder.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c index 6f653444b33..935d81e1161 100644 --- a/programs/winemenubuilder/winemenubuilder.c +++ b/programs/winemenubuilder/winemenubuilder.c @@ -1084,7 +1084,8 @@ static HRESULT platform_write_icon(IStream *icoStream, ICONDIRENTRY *iconDirEntr LARGE_INTEGER zero; *nativeIdentifier = compute_native_identifier(exeIndex, icoPathW, destFilename); - iconsDir = heap_wprintf(L"%s\\icons\\hicolor", xdg_data_dir); + iconsDir = heap_wprintf(L"%s", L"c:\\proton_shortcuts\\icons"); + create_directories(iconsDir); for (i = 0; i < numEntries; i++) { From 0ed5306c1aff5b3ed7c5201dd177163db508114f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 16:27:49 +0300 Subject: [PATCH 458/758] Revert "HACK: proton: configure: Don't build winemenubuilder." This reverts commit 5e25ae85bf50673ec63a8437a7e77fe13dba97aa. --- configure.ac | 1 + loader/wine.inf.in | 1 + 2 files changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index e100509c3f7..14c80983a63 100644 --- a/configure.ac +++ b/configure.ac @@ -3442,6 +3442,7 @@ WINE_CONFIG_MAKEFILE(programs/wineconsole) WINE_CONFIG_MAKEFILE(programs/winedbg) WINE_CONFIG_MAKEFILE(programs/winedevice) WINE_CONFIG_MAKEFILE(programs/winefile) +WINE_CONFIG_MAKEFILE(programs/winemenubuilder) WINE_CONFIG_MAKEFILE(programs/winemine) WINE_CONFIG_MAKEFILE(programs/winemsibuilder) WINE_CONFIG_MAKEFILE(programs/winepath) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 6916d23f178..e7ea49911a1 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2552,6 +2552,7 @@ StartType=3 ErrorControl=1 [Services] +HKLM,%CurrentVersion%\RunServices,"winemenubuilder",2,"%11%\winemenubuilder.exe -a -r" HKLM,"System\CurrentControlSet\Services\Eventlog\Application",,16 HKLM,"System\CurrentControlSet\Services\Eventlog\System","Sources",0x10000,"" HKLM,"System\CurrentControlSet\Services\Tcpip\Parameters","DataBasePath",,"%12%\etc" From 780b866e16575932a48a20644e921e1391d2bcfa Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 19 Jun 2023 16:28:01 +0300 Subject: [PATCH 459/758] wine.inf: Don't use winemenubuilder to create filetype associations. --- loader/wine.inf.in | 1 - 1 file changed, 1 deletion(-) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index e7ea49911a1..6916d23f178 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2552,7 +2552,6 @@ StartType=3 ErrorControl=1 [Services] -HKLM,%CurrentVersion%\RunServices,"winemenubuilder",2,"%11%\winemenubuilder.exe -a -r" HKLM,"System\CurrentControlSet\Services\Eventlog\Application",,16 HKLM,"System\CurrentControlSet\Services\Eventlog\System","Sources",0x10000,"" HKLM,"System\CurrentControlSet\Services\Tcpip\Parameters","DataBasePath",,"%12%\etc" From 65f1dcecebf5a961193ab0b7ab243dcfd03c32b5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 19 Jun 2023 17:50:33 -0600 Subject: [PATCH 460/758] ntdll: Reimplement NtSaveKey() on the client side. CW-Bug-Id: #22347 --- dlls/ntdll/unix/registry.c | 290 ++++++++++++++++++++++++++++++++++++- server/protocol.def | 14 +- server/registry.c | 124 +++++++++++++--- 3 files changed, 399 insertions(+), 29 deletions(-) diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c index d183474b53f..595d4f7892d 100644 --- a/dlls/ntdll/unix/registry.c +++ b/dlls/ntdll/unix/registry.c @@ -27,6 +27,8 @@ #include #include +#include +#include #include "ntstatus.h" #define WIN32_NO_STATUS @@ -68,6 +70,248 @@ NTSTATUS open_hkcu_key( const char *path, HANDLE *key ) return NtCreateKey( key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ); } +/* dump a Unicode string with proper escaping */ +int dump_strW( const WCHAR *str, data_size_t len, FILE *f, const char escape[2] ) +{ + static const char escapes[32] = ".......abtnvfr.............e...."; + char buffer[256]; + char *pos = buffer; + int count = 0; + + for (len /= sizeof(WCHAR); len; str++, len--) + { + if (pos > buffer + sizeof(buffer) - 8) + { + fwrite( buffer, pos - buffer, 1, f ); + count += pos - buffer; + pos = buffer; + } + if (*str > 127) /* hex escape */ + { + if (len > 1 && str[1] < 128 && isxdigit( (char)str[1] )) + pos += sprintf( pos, "\\x%04x", *str ); + else + pos += sprintf( pos, "\\x%x", *str ); + continue; + } + if (*str < 32) /* octal or C escape */ + { + if (!*str && len == 1) continue; /* do not output terminating NULL */ + if (escapes[*str] != '.') + pos += sprintf( pos, "\\%c", escapes[*str] ); + else if (len > 1 && str[1] >= '0' && str[1] <= '7') + pos += sprintf( pos, "\\%03o", *str ); + else + pos += sprintf( pos, "\\%o", *str ); + continue; + } + if (*str == '\\' || *str == escape[0] || *str == escape[1]) *pos++ = '\\'; + *pos++ = *str; + } + fwrite( buffer, pos - buffer, 1, f ); + count += pos - buffer; + return count; +} + +struct saved_key +{ + data_size_t namelen; + WCHAR *name; + data_size_t classlen; + WCHAR *class; + int value_count; + int subkey_count; + unsigned int is_symlink; + timeout_t modif; + struct saved_key *parent; +}; + +/* read serialized key data */ +static char *fill_saved_key( struct saved_key *key, struct saved_key *parent, char *data ) +{ + key->parent = parent; + key->namelen = *(data_size_t *)data; + data += sizeof(data_size_t); + key->name = (WCHAR *)data; + data += key->namelen; + key->classlen = *(data_size_t *)data; + data += sizeof(data_size_t); + key->class = (WCHAR *)data; + data += key->classlen; + key->value_count = *(int *)data; + data += sizeof(int); + key->subkey_count = *(int *)data; + data += sizeof(int); + key->is_symlink = *(unsigned int *)data; + data += sizeof(unsigned int); + key->modif = *(timeout_t *)data; + data += sizeof(timeout_t); + + return data; +} + +/* dump serialized key full path */ +static char *dump_parents( char *data, FILE *f, int count ) +{ + data_size_t len; + WCHAR *name; + + len = *(data_size_t *)data; + data += sizeof(data_size_t); + name = (WCHAR *)data; + data += len; + + if (count > 1) + { + data = dump_parents( data, f, count - 1); + fprintf( f, "\\\\" ); + } + dump_strW( name, len, f, "[]" ); + return data; +} + +/* dump the full path of a key */ +static void dump_path( const struct saved_key *key, const struct saved_key *base, FILE *f ) +{ + if (key->parent && key->parent != base) + { + dump_path( key->parent, base, f ); + fprintf( f, "\\\\" ); + } + dump_strW( key->name, key->namelen, f, "[]" ); +} + +/* dump a value to a text file */ +static char *dump_value( char *data, FILE *f ) +{ + unsigned int i, dw; + int count; + data_size_t namelen, valuelen; + char *valuedata; + WCHAR *name; + unsigned int type; + + namelen = *(data_size_t *)data; + data += sizeof(data_size_t); + name = (WCHAR *)data; + data += namelen; + type = *(unsigned int *)data; + data += sizeof(unsigned int); + valuelen = *(data_size_t *)data; + data += sizeof(data_size_t); + valuedata = data; + data += valuelen; + + if (namelen) + { + fputc( '\"', f ); + count = 1 + dump_strW( name, namelen, f, "\"\"" ); + count += fprintf( f, "\"=" ); + } + else count = fprintf( f, "@=" ); + + switch(type) + { + case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + /* only output properly terminated strings in string format */ + if (valuelen < sizeof(WCHAR)) break; + if (valuelen % sizeof(WCHAR)) break; + if (((WCHAR *)valuedata)[valuelen / sizeof(WCHAR) - 1]) break; + if (type != REG_SZ) fprintf( f, "str(%x):", type ); + fputc( '\"', f ); + dump_strW( (WCHAR *)valuedata, valuelen, f, "\"\"" ); + fprintf( f, "\"\n" ); + return data; + + case REG_DWORD: + if (valuelen != sizeof(dw)) break; + memcpy( &dw, valuedata, sizeof(dw) ); + fprintf( f, "dword:%08x\n", dw ); + return data; + } + + if (type == REG_BINARY) count += fprintf( f, "hex:" ); + else count += fprintf( f, "hex(%x):", type ); + for (i = 0; i < valuelen; i++) + { + count += fprintf( f, "%02x", *((unsigned char *)valuedata + i) ); + if (i < valuelen-1) + { + fputc( ',', f ); + if (++count > 76) + { + fprintf( f, "\\\n " ); + count = 2; + } + } + } + fputc( '\n', f ); + return data; +} + +/* save a registry key and all its subkeys to a text file */ +static char *save_subkeys( char *data, struct saved_key *parent, struct saved_key *base, FILE *f ) +{ + struct saved_key key; + int i; + + if (!base) base = &key; + data = fill_saved_key( &key, parent, data ); + + /* save key if it has either some values or no subkeys, or needs special options */ + /* keys with no values but subkeys are saved implicitly by saving the subkeys */ + if ((key.value_count > 0) || !key.subkey_count || key.classlen || key.is_symlink) + { + fprintf( f, "\n[" ); + if (parent) dump_path( &key, base, f ); + fprintf( f, "] %u\n", (unsigned int)((key.modif - SECS_1601_TO_1970 * TICKSPERSEC) / TICKSPERSEC) ); + fprintf( f, "#time=%x%08x\n", (unsigned int)(key.modif >> 32), (unsigned int)key.modif ); + if (key.classlen) + { + fprintf( f, "#class=\"" ); + dump_strW( key.class, key.classlen, f, "\"\"" ); + fprintf( f, "\"\n" ); + } + if (key.is_symlink) fputs( "#link\n", f ); + for (i = 0; i < key.value_count; i++) data = dump_value( data, f ); + } + for (i = 0; i < key.subkey_count; i++) data = save_subkeys( data, &key, base, f ); + return data; +} + +/* save a registry branch to a file */ +static void save_all_subkeys( char *data, FILE *f ) +{ + enum prefix_type prefix_type; + int parent_count; + + prefix_type = *(int *)data; + data += sizeof(int); + + parent_count = *(int *)data; + data += sizeof(int); + + fprintf( f, "WINE REGISTRY Version 2\n" ); + fprintf( f, ";; All keys relative to " ); + data = dump_parents( data, f, parent_count ); + fprintf( f, "\n" ); + + switch (prefix_type) + { + case PREFIX_32BIT: + fprintf( f, "\n#arch=win32\n" ); + break; + case PREFIX_64BIT: + fprintf( f, "\n#arch=win64\n" ); + break; + default: + break; + } + save_subkeys( data, NULL, NULL, f ); +} + /****************************************************************************** * NtCreateKey (NTDLL.@) @@ -776,17 +1020,53 @@ NTSTATUS WINAPI NtUnloadKey( OBJECT_ATTRIBUTES *attr ) */ NTSTATUS WINAPI NtSaveKey( HANDLE key, HANDLE file ) { + data_size_t size = 0; unsigned int ret; + char *data = NULL; + int fd, fd2, needs_close = 0; + FILE *f; TRACE( "(%p,%p)\n", key, file ); - SERVER_START_REQ( save_registry ) + while (1) { - req->hkey = wine_server_obj_handle( key ); - req->file = wine_server_obj_handle( file ); - ret = wine_server_call( req ); + SERVER_START_REQ( save_registry ) + { + req->hkey = wine_server_obj_handle( key ); + if (size) wine_server_set_reply( req, data, size ); + ret = wine_server_call( req ); + size = reply->total; + } + SERVER_END_REQ; + + if (!ret) break; + free( data ); + if (ret != STATUS_BUFFER_TOO_SMALL) return ret; + if (!(data = malloc( size ))) + { + ERR( "No memory.\n" ); + return STATUS_NO_MEMORY; + } } - SERVER_END_REQ; + + if ((ret = server_get_unix_fd( file, FILE_WRITE_DATA, &fd, &needs_close, NULL, NULL ))) goto done; + if ((fd2 = dup( fd )) == -1) + { + ret = errno_to_status( errno ); + goto done; + } + if (!(f = fdopen( fd2, "w" ))) + { + close( fd2 ); + ret = errno_to_status( errno ); + goto done; + } + save_all_subkeys( data, f ); + if (fclose(f)) ret = errno_to_status( errno ); + +done: + if (needs_close) close( fd ); + free( data ); return ret; } diff --git a/server/protocol.def b/server/protocol.def index 3f8e8118aca..8e9fd62c64e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1917,11 +1917,19 @@ struct process_info @END -/* Save a registry branch to a file */ +/* Return full registry branch non-volatile data for saving */ @REQ(save_registry) - obj_handle_t hkey; /* key to save */ - obj_handle_t file; /* file to save to */ + obj_handle_t hkey; /* key to save */ +@REPLY + data_size_t total; /* total length needed for data */ + VARARG(data,bytes); /* registry data */ @END +enum prefix_type +{ + PREFIX_UNKNOWN, + PREFIX_32BIT, + PREFIX_64BIT, +}; /* Add a registry key change notification */ diff --git a/server/registry.c b/server/registry.c index b7761802f46..f2d2b64d4d9 100644 --- a/server/registry.c +++ b/server/registry.c @@ -124,7 +124,7 @@ static struct key *root_key; static const timeout_t ticks_1601_to_1970 = (timeout_t)86400 * (369 * 365 + 89) * TICKS_PER_SEC; static const timeout_t save_period = 30 * -TICKS_PER_SEC; /* delay between periodic saves */ static struct timeout_user *save_timeout_user; /* saving timer */ -static enum prefix_type { PREFIX_UNKNOWN, PREFIX_32BIT, PREFIX_64BIT } prefix_type; +static enum prefix_type prefix_type; static const WCHAR wow6432node[] = {'W','o','w','6','4','3','2','N','o','d','e'}; static const WCHAR symlink_value[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e'}; @@ -2022,29 +2022,104 @@ static void save_all_subkeys( struct key *key, FILE *f ) save_subkeys( key, key, f ); } -/* save a registry branch to a file handle */ -static void save_registry( struct key *key, obj_handle_t handle ) +static data_size_t serialize_value( const struct key_value *value, char *buf ) { - struct file *file; - int fd; + data_size_t size; - if (!(file = get_file_obj( current->process, handle, FILE_WRITE_DATA ))) return; - fd = dup( get_file_unix_fd( file ) ); - release_object( file ); - if (fd != -1) + size = sizeof(data_size_t) + value->namelen + sizeof(unsigned int) + sizeof(data_size_t) + value->len; + if (!buf) return size; + + *(data_size_t *)buf = value->namelen; + buf += sizeof(data_size_t); + memcpy( buf, value->name, value->namelen ); + buf += value->namelen; + + *(unsigned int *)buf = value->type; + buf += sizeof(unsigned int); + + *(data_size_t *)buf = value->len; + buf += sizeof(data_size_t); + memcpy( buf, value->data, value->len ); + + return size; +} + +/* save a registry key with subkeys to a buffer */ +static data_size_t serialize_key( const struct key *key, char *buf ) +{ + data_size_t size; + int subkey_count, i; + + if (key->flags & KEY_VOLATILE) return 0; + + size = sizeof(data_size_t) + key->obj.name->len + sizeof(data_size_t) + key->classlen + sizeof(int) + sizeof(int) + + sizeof(unsigned int) + sizeof(timeout_t); + for (i = 0; i <= key->last_value; i++) + size += serialize_value( &key->values[i], buf ? buf + size : NULL ); + subkey_count = 0; + for (i = 0; i <= key->last_subkey; i++) { - FILE *f = fdopen( fd, "w" ); - if (f) - { - save_all_subkeys( key, f ); - if (fclose( f )) file_set_error(); - } - else - { - file_set_error(); - close( fd ); - } + if (key->subkeys[i]->flags & KEY_VOLATILE) continue; + size += serialize_key( key->subkeys[i], buf ? buf + size : NULL ); + ++subkey_count; + } + if (!buf) return size; + + *(data_size_t *)buf = key->obj.name->len; + buf += sizeof(data_size_t); + memcpy( buf, key->obj.name->name, key->obj.name->len ); + buf += key->obj.name->len; + + *(data_size_t *)buf = key->classlen; + buf += sizeof(data_size_t); + memcpy( buf, key->class, key->classlen ); + buf += key->classlen; + + *(int *)buf = key->last_value + 1; + buf += sizeof(int); + + *(int *)buf = subkey_count; + buf += sizeof(int); + + *(unsigned int *)buf = key->flags & KEY_SYMLINK; + buf += sizeof(unsigned int); + + *(timeout_t *)buf = key->modif; + + return size; +} + +/* save registry branch to buffer */ +static data_size_t save_registry( const struct key *key, char *buf ) +{ + int *parent_count = NULL; + const struct key *parent; + data_size_t size; + + size = sizeof(int) + sizeof(int); + if (buf) + { + *(int *)buf = prefix_type; + buf += sizeof(int); + parent_count = (int *)buf; + buf += sizeof(int); + *parent_count = 0; } + + parent = key; + do + { + size += sizeof(data_size_t) + parent->obj.name->len; + if (!buf) continue; + ++*parent_count; + *(data_size_t *)buf = parent->obj.name->len; + buf += sizeof(data_size_t); + memcpy( buf, parent->obj.name->name, parent->obj.name->len ); + buf += parent->obj.name->len; + } while ((parent = get_parent( parent ))); + + size += serialize_key( key, buf ); + return size; } /* save a registry branch to a file */ @@ -2371,6 +2446,7 @@ DECL_HANDLER(unload_registry) DECL_HANDLER(save_registry) { struct key *key; + char *data; if (!thread_single_check_privilege( current, SeBackupPrivilege )) { @@ -2380,7 +2456,13 @@ DECL_HANDLER(save_registry) if ((key = get_hkey_obj( req->hkey, 0 ))) { - save_registry( key, req->file ); + reply->total = save_registry( key, NULL ); + if (reply->total <= get_reply_max_size()) + { + if ((data = set_reply_data_size( reply->total ))) + save_registry( key, data ); + } + else set_error( STATUS_BUFFER_TOO_SMALL ); release_object( key ); } } From 0823916c9ea7f0220525878cb6fb9304a72bfe95 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 20 Jun 2023 11:54:06 -0600 Subject: [PATCH 461/758] ntdll: Implement NtFlushKey(). CW-Bug-Id: #22347 --- dlls/ntdll/unix/registry.c | 155 +++++++++++++++++++++++++++++++++++-- server/protocol.def | 12 +++ server/registry.c | 94 ++++++++++++++++++++-- 3 files changed, 247 insertions(+), 14 deletions(-) diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c index 595d4f7892d..d61c9f21732 100644 --- a/dlls/ntdll/unix/registry.c +++ b/dlls/ntdll/unix/registry.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "ntstatus.h" #define WIN32_NO_STATUS @@ -282,8 +284,9 @@ static char *save_subkeys( char *data, struct saved_key *parent, struct saved_ke } /* save a registry branch to a file */ -static void save_all_subkeys( char *data, FILE *f ) +static char *save_all_subkeys( char *data, FILE *f ) { + /* Output registry format should match server/registry.c:save_all_subkeys(). */ enum prefix_type prefix_type; int parent_count; @@ -309,7 +312,7 @@ static void save_all_subkeys( char *data, FILE *f ) default: break; } - save_subkeys( data, NULL, NULL, f ); + return save_subkeys( data, NULL, NULL, f ); } @@ -901,22 +904,162 @@ NTSTATUS WINAPI NtNotifyChangeKey( HANDLE key, HANDLE event, PIO_APC_ROUTINE apc io, filter, subtree, buffer, length, async ); } +/* acquire mutex for registry flush operation */ +static HANDLE get_key_flush_mutex(void) +{ + WCHAR bufferW[256]; + UNICODE_STRING name = {.Buffer = bufferW}; + OBJECT_ATTRIBUTES attr; + char buffer[256]; + HANDLE mutex; + + snprintf( buffer, ARRAY_SIZE(buffer), "\\Sessions\\%u\\BaseNamedObjects\\__wine_regkey_flush", + (int)NtCurrentTeb()->Peb->SessionId ); + name.Length = name.MaximumLength = (strlen(buffer) + 1) * sizeof(WCHAR); + ascii_to_unicode( bufferW, buffer, name.Length / sizeof(WCHAR) ); + + InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, NULL, NULL ); + if (NtCreateMutant( &mutex, MUTEX_ALL_ACCESS, &attr, FALSE ) < 0) return NULL; + NtWaitForSingleObject( mutex, FALSE, NULL ); + return mutex; +} + +/* release registry flush mutex */ +static void release_key_flush_mutex( HANDLE mutex ) +{ + NtReleaseMutant( mutex, NULL ); + NtClose( mutex ); +} + +/* save registry branch to Wine regsitry storage file */ +static NTSTATUS save_registry_branch( char **data ) +{ + static const char temp_fn[] = "savereg.tmp"; + char *file_name, *path = NULL, *tmp = NULL; + int file_name_len, path_len, fd; + struct stat st; + NTSTATUS ret; + FILE *f; + + file_name_len = *(int *)*data; + *data += sizeof(int); + file_name = *data; + *data += file_name_len; + + path_len = strlen( config_dir ) + 1 + file_name_len + 1; + if (!(path = malloc( path_len ))) return STATUS_NO_MEMORY; + sprintf( path, "%s/%s", config_dir, file_name ); + + if ((fd = open( path, O_WRONLY )) != -1) + { + /* if file is not a regular file or has multiple links or is accessed + * via symbolic links, write directly into it; otherwise use a temp file */ + if (!lstat( path, &st ) && (!S_ISREG(st.st_mode) || st.st_nlink > 1)) + { + ftruncate( fd, 0 ); + goto save; + } + close( fd ); + } + + /* create a temp file in the same directory */ + if (!(tmp = malloc( strlen( config_dir ) + 1 + strlen( temp_fn ) + 1 ))) + { + ret = STATUS_NO_MEMORY; + goto done; + } + sprintf( tmp, "%s/%s", config_dir, temp_fn ); + + if ((fd = open( tmp, O_CREAT | O_EXCL | O_WRONLY, 0666 )) == -1) + { + ret = errno_to_status( errno ); + goto done; + } + +save: + if (!(f = fdopen( fd, "w" ))) + { + ret = errno_to_status( errno ); + if (tmp) unlink( tmp ); + close( fd ); + goto done; + } + + *data = save_all_subkeys( *data, f ); + + ret = fclose( f ) ? errno_to_status( errno ) : STATUS_SUCCESS; + if (tmp) + { + if (!ret && rename( tmp, path )) ret = errno_to_status( errno ); + if (ret) unlink( tmp ); + } + +done: + free( tmp ); + free( path ); + return ret; +} /****************************************************************************** * NtFlushKey (NTDLL.@) */ NTSTATUS WINAPI NtFlushKey( HANDLE key ) { + abstime_t timestamp_counter; + data_size_t size = 0; unsigned int ret; + char *data = NULL, *curr_data; + HANDLE mutex; + int i, branch_count, branch; TRACE( "key=%p\n", key ); - SERVER_START_REQ( flush_key ) + mutex = get_key_flush_mutex(); + + while (1) { - req->hkey = wine_server_obj_handle( key ); - ret = wine_server_call( req ); + SERVER_START_REQ( flush_key ) + { + req->hkey = wine_server_obj_handle( key ); + if (size) wine_server_set_reply( req, data, size ); + ret = wine_server_call( req ); + size = reply->total; + branch_count = reply->branch_count; + timestamp_counter = reply->timestamp_counter; + } + SERVER_END_REQ; + + if (ret != STATUS_BUFFER_TOO_SMALL) break; + free( data ); + if (!(data = malloc( size ))) + { + ERR( "No memory.\n" ); + ret = STATUS_NO_MEMORY; + goto done; + } } - SERVER_END_REQ; + if (ret) goto done; + + curr_data = data; + for (i = 0; i < branch_count; ++i) + { + branch = *(int *)curr_data; + curr_data += sizeof(int); + if ((ret = save_registry_branch( &curr_data ))) goto done; + + SERVER_START_REQ( flush_key_done ) + { + req->branch = branch; + req->timestamp_counter = timestamp_counter; + ret = wine_server_call( req ); + } + SERVER_END_REQ; + if (ret) break; + } + +done: + release_key_flush_mutex( mutex ); + free( data ); return ret; } diff --git a/server/protocol.def b/server/protocol.def index 8e9fd62c64e..9589aacfbbc 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1837,6 +1837,18 @@ struct process_info /* Flush a registry key */ @REQ(flush_key) obj_handle_t hkey; /* handle to the key */ +@REPLY + abstime_t timestamp_counter; /* branch last change timestamp counter */ + data_size_t total; /* total length needed for data */ + int branch_count; /* number of registry branches to flush */ + VARARG(data,bytes); /* registry data */ +@END + + +/* Clear KEY_DIRTY after key flush */ +@REQ(flush_key_done) + abstime_t timestamp_counter; /* timestamp counter returned from flush_key */ + int branch; /* saved registry branch id */ @END diff --git a/server/registry.c b/server/registry.c index f2d2b64d4d9..ddfa78a155e 100644 --- a/server/registry.c +++ b/server/registry.c @@ -90,6 +90,7 @@ struct key unsigned int flags; /* flags */ timeout_t modif; /* last modification time */ struct list notify_list; /* list of notifications */ + abstime_t timestamp_counter; /* timestamp counter at last change */ }; /* key flags */ @@ -118,6 +119,8 @@ struct key_value #define MAX_NAME_LEN 256 /* max. length of a key name */ #define MAX_VALUE_LEN 16383 /* max. length of a value name */ +static abstime_t change_timestamp_counter; + /* the root of the registry tree */ static struct key *root_key; @@ -710,6 +713,7 @@ static struct key *create_key_object( struct object *parent, const struct unicod key->last_value = -1; key->values = NULL; key->modif = modif; + key->timestamp_counter = 0; list_init( &key->notify_list ); if (options & REG_OPTION_CREATE_LINK) key->flags |= KEY_SYMLINK; @@ -730,23 +734,25 @@ static struct key *create_key_object( struct object *parent, const struct unicod /* mark a key and all its parents as dirty (modified) */ static void make_dirty( struct key *key ) { + ++change_timestamp_counter; while (key) { if (key->flags & (KEY_DIRTY|KEY_VOLATILE)) return; /* nothing to do */ key->flags |= KEY_DIRTY; + key->timestamp_counter = change_timestamp_counter; key = get_parent( key ); } } /* mark a key and all its subkeys as clean (not modified) */ -static void make_clean( struct key *key ) +static void make_clean( struct key *key, abstime_t timestamp_counter ) { int i; if (key->flags & KEY_VOLATILE) return; if (!(key->flags & KEY_DIRTY)) return; - key->flags &= ~KEY_DIRTY; - for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i] ); + if (key->timestamp_counter <= timestamp_counter) key->flags &= ~KEY_DIRTY; + for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i], timestamp_counter ); } /* go through all the notifications and send them if necessary */ @@ -2004,6 +2010,7 @@ void init_registry(void) /* save a registry branch to a file */ static void save_all_subkeys( struct key *key, FILE *f ) { + /* Registry format in ntdll/registry.c:save_all_subkeys() should match. */ fprintf( f, "WINE REGISTRY Version 2\n" ); fprintf( f, ";; All keys relative to " ); dump_path( key, NULL, f ); @@ -2192,7 +2199,7 @@ static int save_branch( struct key *key, const char *path ) done: free( tmp ); - if (ret) make_clean( key ); + if (ret) make_clean( key, key->timestamp_counter ); return ret; } @@ -2240,6 +2247,36 @@ static int is_wow64_thread( struct thread *thread ) return (is_machine_64bit( native_machine ) && !is_machine_64bit( thread->process->machine )); } +/* find all the branches inside the specified key or the branch containing the key */ +static void find_branches_for_key( struct key *key, int *branches, int *branch_count ) +{ + struct key *k; + int i; + + *branch_count = 0; + for (i = 0; i < save_branch_count; i++) + { + k = save_branch_info[i].key; + while ((k = get_parent(k))) + { + if (k != key) continue; + branches[(*branch_count)++] = i; + break; + } + } + + if (*branch_count) return; + + do + { + for (i = 0; i < save_branch_count; i++) + { + if(key != save_branch_info[i].key) continue; + branches[(*branch_count)++] = i; + return; + } + } while ((key = get_parent( key ))); +} /* create a registry key */ DECL_HANDLER(create_key) @@ -2304,15 +2341,56 @@ DECL_HANDLER(delete_key) } } -/* flush a registry key */ +/* return registry branches snaphot data for flushing key */ DECL_HANDLER(flush_key) { struct key *key = get_hkey_obj( req->hkey, 0 ); - if (key) + int branches[3], branch_count = 0, i, path_len; + char *data; + + if (!key) return; + + reply->total = 0; + reply->branch_count = 0; + if ((key->flags & KEY_DIRTY) && !(key->flags & KEY_VOLATILE)) + find_branches_for_key( key, branches, &branch_count ); + release_object( key ); + + reply->timestamp_counter = change_timestamp_counter; + for (i = 0; i < branch_count; ++i) { - /* we don't need to do anything here with the current implementation */ - release_object( key ); + if (!(save_branch_info[branches[i]].key->flags & KEY_DIRTY)) continue; + ++reply->branch_count; + path_len = strlen( save_branch_info[branches[i]].path ) + 1; + reply->total += sizeof(int) + sizeof(int) + path_len + save_registry( save_branch_info[branches[i]].key, NULL ); + } + if (reply->total > get_reply_max_size()) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return; } + + if (!(data = set_reply_data_size( reply->total ))) return; + + for (i = 0; i < branch_count; ++i) + { + if (!(save_branch_info[branches[i]].key->flags & KEY_DIRTY)) continue; + *(int *)data = branches[i]; + data += sizeof(int); + path_len = strlen( save_branch_info[branches[i]].path ) + 1; + *(int *)data = path_len; + data += sizeof(int); + memcpy( data, save_branch_info[branches[i]].path, path_len ); + data += path_len; + data += save_registry( save_branch_info[branches[i]].key, data ); + } +} + +/* clear dirty state after successful registry branch flush */ +DECL_HANDLER(flush_key_done) +{ + if (req->branch < save_branch_count) make_clean( save_branch_info[req->branch].key, req->timestamp_counter ); + else set_error( STATUS_INVALID_PARAMETER ); } /* enumerate registry subkeys */ From f2c5cb28b458f4bfcd78e0a8edea9831411d9693 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 20 Jun 2023 12:17:36 -0600 Subject: [PATCH 462/758] mountmgr.sys: Perform periodic registry flush instead of server. CW-Bug-Id: #22347 --- dlls/mountmgr.sys/mountmgr.c | 22 ++++++++++++++++++++++ server/registry.c | 26 -------------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c index 9f72eedb33b..a04449a6aa1 100644 --- a/dlls/mountmgr.sys/mountmgr.c +++ b/dlls/mountmgr.sys/mountmgr.c @@ -611,6 +611,27 @@ static DWORD WINAPI run_loop_thread( void *arg ) return MOUNTMGR_CALL( run_loop, ¶ms ); } +static DWORD WINAPI registry_flush_thread( void *arg ) +{ + UNICODE_STRING name = RTL_CONSTANT_STRING( L"\\Registry" ); + OBJECT_ATTRIBUTES attr; + HANDLE root; + + InitializeObjectAttributes( &attr, &name, 0, 0, NULL ); + if (NtOpenKeyEx( &root, MAXIMUM_ALLOWED, &attr, 0 )) + { + ERR( "Failed opening root registry key.\n" ); + return 0; + } + + for (;;) + { + Sleep( 30000 ); + if (NtFlushKey( root )) ERR( "Failed flushing registry.\n" ); + } + + return 0; +} /* main entry point for the mount point manager driver */ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) @@ -653,6 +674,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) thread = CreateThread( NULL, 0, device_op_thread, NULL, 0, NULL ); CloseHandle( CreateThread( NULL, 0, run_loop_thread, thread, 0, NULL )); + CloseHandle( CreateThread( NULL, 0, registry_flush_thread, thread, 0, NULL )); #ifdef _WIN64 /* create a symlink so that the Wine port overrides key can be edited with 32-bit reg or regedit */ diff --git a/server/registry.c b/server/registry.c index ddfa78a155e..c3cf9ba7dfa 100644 --- a/server/registry.c +++ b/server/registry.c @@ -125,15 +125,12 @@ static abstime_t change_timestamp_counter; static struct key *root_key; static const timeout_t ticks_1601_to_1970 = (timeout_t)86400 * (369 * 365 + 89) * TICKS_PER_SEC; -static const timeout_t save_period = 30 * -TICKS_PER_SEC; /* delay between periodic saves */ -static struct timeout_user *save_timeout_user; /* saving timer */ static enum prefix_type prefix_type; static const WCHAR wow6432node[] = {'W','o','w','6','4','3','2','N','o','d','e'}; static const WCHAR symlink_value[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e'}; static const struct unicode_str symlink_str = { symlink_value, sizeof(symlink_value) }; -static void set_periodic_save_timer(void); static struct key_value *find_value( const struct key *key, const struct unicode_str *name, int *index ); /* information about where to save a registry branch */ @@ -1983,9 +1980,6 @@ void init_registry(void) release_object( hklm ); release_object( hkcu ); - /* start the periodic save timer */ - set_periodic_save_timer(); - /* create windows directories */ if (!mkdir( "drive_c/windows", 0777 )) @@ -2203,26 +2197,6 @@ static int save_branch( struct key *key, const char *path ) return ret; } -/* periodic saving of the registry */ -static void periodic_save( void *arg ) -{ - int i; - - if (fchdir( config_dir_fd ) == -1) return; - save_timeout_user = NULL; - for (i = 0; i < save_branch_count; i++) - save_branch( save_branch_info[i].key, save_branch_info[i].path ); - if (fchdir( server_dir_fd ) == -1) fatal_error( "chdir to server dir: %s\n", strerror( errno )); - set_periodic_save_timer(); -} - -/* start the periodic save timer */ -static void set_periodic_save_timer(void) -{ - if (save_timeout_user) remove_timeout_user( save_timeout_user ); - save_timeout_user = add_timeout_user( save_period, periodic_save, NULL ); -} - /* save the modified registry branches to disk */ void flush_registry(void) { From b05df8a38652a315782474df478635c1e59cd93e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 21 Jun 2023 11:06:10 +0200 Subject: [PATCH 463/758] kernelbase: Free unix console in AllocConsole. --- dlls/kernelbase/console.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c index 38d09d6c60f..706d83b79eb 100644 --- a/dlls/kernelbase/console.c +++ b/dlls/kernelbase/console.c @@ -453,6 +453,15 @@ static BOOL alloc_console( BOOL headless ) */ BOOL WINAPI AllocConsole(void) { + RTL_USER_PROCESS_PARAMETERS *params = RtlGetCurrentPeb()->ProcessParameters; + + /* allow gui applications to create a genuine console over a unix one */ + if (RtlImageNtHeader( GetModuleHandleW( NULL ) )->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI && + (params->ConsoleHandle == CONSOLE_HANDLE_SHELL_NO_WINDOW || + console_ioctl( params->ConsoleHandle, IOCTL_CONDRV_IS_UNIX, NULL, 0, NULL, 0, NULL ))) + { + FreeConsole(); + } return alloc_console( FALSE ); } From 95ab7a2152d91e9f4ab72b3c3b9262dd099c6196 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty Date: Thu, 22 Jun 2023 00:11:34 -0400 Subject: [PATCH 464/758] wine.inf: Set msvcp140_atomic_wait to native,builtin. --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 6916d23f178..d642cadfe2e 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2828,6 +2828,7 @@ HKCU,Software\Wine\DllOverrides,"api-ms-win-crt-time-l1-1-0",0x2,"native,builtin HKCU,Software\Wine\DllOverrides,"atl140",0x2,"native,builtin" HKCU,Software\Wine\DllOverrides,"concrt140",0x2,"native,builtin" HKCU,Software\Wine\DllOverrides,"msvcp140",0x2,"native,builtin" +HKCU,Software\Wine\DllOverrides,"msvcp140_atomic_wait",0x2,"native,builtin" HKCU,Software\Wine\DllOverrides,"msvcr140",0x2,"native,builtin" HKCU,Software\Wine\DllOverrides,"ucrtbase",0x2,"native,builtin" HKCU,Software\Wine\DllOverrides,"vcomp140",0x2,"native,builtin" From bff9dbbeda645d7e3c49e5d9053455c2cd98cb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:40:34 +0200 Subject: [PATCH 465/758] Revert "winex11.drv: Cache clip window." This reverts commit 671e9f996519cc6bbfecc7fafee9eb759ad2d2a2. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 41 +++++++++------------------------------ dlls/winex11.drv/x11drv.h | 1 - 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 739960b083e..4142536b96f 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -152,34 +152,6 @@ MAKE_FUNCPTR(XGetDeviceButtonMapping); #undef MAKE_FUNCPTR #endif -static HWND get_clip_hwnd(void) -{ - static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0}; - struct x11drv_thread_data *data = x11drv_thread_data(); - UNICODE_STRING class_name; - HWND ret; - - if (data->cached_clip_hwnd) - { - ret = data->cached_clip_hwnd; - data->cached_clip_hwnd = NULL; - return ret; - } - RtlInitUnicodeString( &class_name, messageW ); - return NtUserCreateWindowEx( 0, &class_name, &class_name, NULL, 0, 0, 0, 0, 0, - HWND_MESSAGE, 0, NtCurrentTeb()->Peb->ImageBaseAddress, - NULL, 0, NULL, 0, FALSE ); -} - -static void release_clip_hwnd( HWND hwnd ) -{ - struct x11drv_thread_data *data = x11drv_thread_data(); - - if (data->cached_clip_hwnd) - NtUserDestroyWindow( data->cached_clip_hwnd ); - data->cached_clip_hwnd = hwnd; -} - /*********************************************************************** * X11DRV_Xcursor_Init * @@ -464,7 +436,9 @@ void X11DRV_XInput2_Enable( Display *display, Window window, long event_mask ) static BOOL grab_clipping_window( const RECT *clip ) { #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H + static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0}; struct x11drv_thread_data *data = x11drv_thread_data(); + UNICODE_STRING class_name; Window clip_window; HWND msg_hwnd = 0; POINT pos; @@ -476,7 +450,10 @@ static BOOL grab_clipping_window( const RECT *clip ) if (!data) return FALSE; if (!(clip_window = init_clip_window())) return TRUE; - if (!(msg_hwnd = get_clip_hwnd())) + RtlInitUnicodeString( &class_name, messageW ); + if (!(msg_hwnd = NtUserCreateWindowEx( 0, &class_name, &class_name, NULL, 0, 0, 0, 0, 0, + HWND_MESSAGE, 0, NtCurrentTeb()->Peb->ImageBaseAddress, + NULL, 0, NULL, 0, FALSE ))) return TRUE; /* enable XInput2 unless we are already clipping */ @@ -515,7 +492,7 @@ static BOOL grab_clipping_window( const RECT *clip ) if (!clipping_cursor) { X11DRV_XInput2_Enable( data->display, None, 0 ); - release_clip_hwnd( msg_hwnd ); + NtUserDestroyWindow( msg_hwnd ); return FALSE; } clip_rect = *clip; @@ -588,7 +565,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) data->clip_hwnd = 0; data->clip_reset = NtGetTickCount(); X11DRV_XInput2_Enable( data->display, None, 0 ); - release_clip_hwnd( hwnd ); + NtUserDestroyWindow( hwnd ); } else if (prev_clip_hwnd) { @@ -596,7 +573,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) * dangling clip window. */ TRACE( "destroying old clip hwnd %p\n", prev_clip_hwnd ); - release_clip_hwnd( prev_clip_hwnd ); + NtUserDestroyWindow( prev_clip_hwnd ); } return 0; } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index e6fd5ef940b..0bc79bd7a4d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -402,7 +402,6 @@ struct x11drv_thread_data int xi2_active_touches; int xi2_primary_touchid; #endif /* HAVE_X11_EXTENSIONS_XINPUT2_H */ - HWND cached_clip_hwnd; }; extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN; From 28509a6c8d3dbd31bae7713830876105335e6528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:17:19 +0200 Subject: [PATCH 466/758] Revert "winex11.drv: Flush X connection after ungrabbing the pointer." This reverts commit 4866e64ec2877b0b91da8f745ed550127f8fb59e. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 4142536b96f..ba16ea861be 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -521,11 +521,7 @@ void ungrab_clipping_window(void) TRACE( "no longer clipping\n" ); XUnmapWindow( display, clip_window ); - if (clipping_cursor) - { - XUngrabPointer( display, CurrentTime ); - XFlush( display ); - } + if (clipping_cursor) XUngrabPointer( display, CurrentTime ); clipping_cursor = FALSE; send_notify_message( NtUserGetDesktopWindow(), WM_X11DRV_CLIP_CURSOR_NOTIFY, 0, 0 ); } From 91e23be042696cbe1f9a929ab5f331de4cd76ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 26 Jun 2023 10:59:45 +0200 Subject: [PATCH 467/758] Revert "winex11.drv: Send missed KEYUP events on KeymapNotify." This reverts commit f7199f591890c5f826fe52ddbdf9f01a19f44dc9. --- dlls/winex11.drv/event.c | 2 -- dlls/winex11.drv/keyboard.c | 43 ++----------------------------------- dlls/winex11.drv/mouse.c | 2 -- dlls/winex11.drv/x11drv.h | 1 - 4 files changed, 2 insertions(+), 46 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 91329a39167..0a27fbfb065 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -898,8 +898,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE; - x11drv_thread_data()->keymapnotify_hwnd = hwnd; - /* Focus was just restored but it can be right after super was * pressed and gnome-shell needs a bit of time to respond and * toggle the activity view. If we grab the cursor right away diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index f476919f9f5..1d6cef40efb 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1195,19 +1195,11 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) int i, j; BYTE keystate[256]; WORD vkey; - DWORD flags; - KeyCode keycode; - HWND keymapnotify_hwnd; BOOL changed = FALSE; struct { WORD vkey; - WORD scan; WORD pressed; } keys[256]; - struct x11drv_thread_data *thread_data = x11drv_thread_data(); - - keymapnotify_hwnd = thread_data->keymapnotify_hwnd; - thread_data->keymapnotify_hwnd = NULL; if (!get_async_key_state( keystate )) return FALSE; @@ -1222,17 +1214,11 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) { for (j = 0; j < 8; j++) { - keycode = (i * 8) + j; - vkey = keyc2vkey[keycode]; + vkey = keyc2vkey[(i * 8) + j]; /* If multiple keys map to the same vkey, we want to report it as * pressed iff any of them are pressed. */ - if (!keys[vkey & 0xff].vkey) - { - keys[vkey & 0xff].vkey = vkey; - keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; - } - + if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); - x11drv_thread_data()->keymapnotify_hwnd = hwnd; - if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; /* simulate a mouse motion event */ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 0bc79bd7a4d..5f8cef45928 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -385,7 +385,6 @@ struct x11drv_thread_data XEvent *current_event; /* event currently being processed */ HWND grab_hwnd; /* window that currently grabs the mouse */ HWND last_focus; /* last window that had focus */ - HWND keymapnotify_hwnd; /* window that should receive modifier release events */ XIM xim; /* input method */ HWND last_xic_hwnd; /* last xic window */ XFontSet font_set; /* international text drawing font set */ From 476c74cd93bc0a6ebde062db22be9e39729a53aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:10 +0200 Subject: [PATCH 468/758] Revert "HACK: mutter: winex11.drv: Add a bit of delay before restoring mouse grabs on FocusIn." This reverts commit 0063bac7b18e10547de2ca1125f62ebbb98dd16b. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 0a27fbfb065..755dfc6506d 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -898,17 +898,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE; - /* Focus was just restored but it can be right after super was - * pressed and gnome-shell needs a bit of time to respond and - * toggle the activity view. If we grab the cursor right away - * it will cancel it and super key will do nothing. - */ - if (event->mode == NotifyUngrab && wm_is_mutter(event->display)) - { - LARGE_INTEGER timeout = {.QuadPart = 100 * -10000}; - NtDelayExecution( FALSE, &timeout ); - } - if (!try_grab_pointer( event->display )) { /* ask the desktop window to release its grab before trying to get ours */ From c63f76bf5f77505e32fe7ec1a9d8fda11c8341b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:17:48 +0200 Subject: [PATCH 469/758] Revert "Revert "winex11.drv: Only call XWarpPointer if we can get exclusive pointer grab."" This reverts commit 7dc685f645fff8a6215006b42deb8fa8a1329a6e. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 7d6e482d740..d3950b7d7d3 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1569,10 +1569,23 @@ BOOL X11DRV_SetCursorPos( INT x, INT y ) struct x11drv_thread_data *data = x11drv_init_thread_data(); POINT pos = virtual_screen_to_root( x, y ); + if (!clipping_cursor && + XGrabPointer( data->display, root_window, False, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime ) != GrabSuccess) + { + WARN( "refusing to warp pointer to %u, %u without exclusive grab\n", (int)pos.x, (int)pos.y ); + return FALSE; + } + TRACE( "real setting to %s\n", wine_dbgstr_point( &pos ) ); XWarpPointer( data->display, root_window, root_window, 0, 0, 0, 0, pos.x, pos.y ); data->warp_serial = NextRequest( data->display ); + + if (!clipping_cursor) + XUngrabPointer( data->display, CurrentTime ); + XNoOp( data->display ); XFlush( data->display ); /* avoids bad mouse lag in games that do their own mouse warping */ TRACE( "warped to (fake) %d,%d serial %lu\n", x, y, data->warp_serial ); From 384ff312d4114ad5e164edab908314e24e66ba1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:18 +0200 Subject: [PATCH 470/758] Revert "Revert "winex11.drv: Only grab or warp the cursor when keyboard isn't grabbed."" This reverts commit d3b5338ca388aa26374fed748ef2c493dd173609. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 37 +++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/mouse.c | 12 ++++++++++++ dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 50 insertions(+) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 755dfc6506d..84f72176665 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -145,6 +145,9 @@ static const char * event_names[MAX_EVENT_HANDLERS] = "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent" }; +/* is someone else grabbing the keyboard, for example the WM, when manipulating the window */ +BOOL keyboard_grabbed = FALSE; + int xinput2_opcode = 0; static pthread_mutex_t input_cs = PTHREAD_MUTEX_INITIALIZER; @@ -898,6 +901,23 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE; + switch (event->mode) + { + case NotifyGrab: + /* these are received when moving undecorated managed windows on mutter */ + keyboard_grabbed = TRUE; + break; + case NotifyWhileGrabbed: + keyboard_grabbed = TRUE; + break; + case NotifyNormal: + keyboard_grabbed = FALSE; + break; + case NotifyUngrab: + keyboard_grabbed = FALSE; + break; + } + if (!try_grab_pointer( event->display )) { /* ask the desktop window to release its grab before trying to get ours */ @@ -1023,6 +1043,23 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) } if (!hwnd) return FALSE; + switch (event->mode) + { + case NotifyUngrab: + /* these are received when moving undecorated managed windows on mutter */ + keyboard_grabbed = FALSE; + break; + case NotifyNormal: + keyboard_grabbed = FALSE; + break; + case NotifyWhileGrabbed: + keyboard_grabbed = TRUE; + break; + case NotifyGrab: + keyboard_grabbed = TRUE; + break; + } + if (hwnd == NtUserGetForegroundWindow()) ungrab_clipping_window(); /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index d3950b7d7d3..0e4a87e6cd6 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -456,6 +456,12 @@ static BOOL grab_clipping_window( const RECT *clip ) NULL, 0, NULL, 0, FALSE ))) return TRUE; + if (keyboard_grabbed) + { + WARN( "refusing to clip to %s\n", wine_dbgstr_rect(clip) ); + return FALSE; + } + /* enable XInput2 unless we are already clipping */ if (!data->clip_hwnd) X11DRV_XInput2_Enable( data->display, None, PointerMotionMask ); @@ -1569,6 +1575,12 @@ BOOL X11DRV_SetCursorPos( INT x, INT y ) struct x11drv_thread_data *data = x11drv_init_thread_data(); POINT pos = virtual_screen_to_root( x, y ); + if (keyboard_grabbed) + { + WARN( "refusing to warp to %u, %u\n", (int)pos.x, (int)pos.y ); + return FALSE; + } + if (!clipping_cursor && XGrabPointer( data->display, root_window, False, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5f8cef45928..1d4e2f75486 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -437,6 +437,7 @@ extern Colormap default_colormap DECLSPEC_HIDDEN; extern XPixmapFormatValues **pixmap_formats DECLSPEC_HIDDEN; extern Window root_window DECLSPEC_HIDDEN; extern BOOL clipping_cursor DECLSPEC_HIDDEN; +extern BOOL keyboard_grabbed DECLSPEC_HIDDEN; extern unsigned int screen_bpp DECLSPEC_HIDDEN; extern BOOL usexrandr DECLSPEC_HIDDEN; extern BOOL usexvidmode DECLSPEC_HIDDEN; From 36245da6ed28818a72cbaf245428df7f12243d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:22 +0200 Subject: [PATCH 471/758] Revert "winex11.drv: Restore pointer grab on FocusIn events." This reverts commit 48294dc6b2b35cac26badd08f07306b416e0aba0. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 13 +++++++------ dlls/winex11.drv/mouse.c | 24 ++++++++++++++++++++++++ dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 84f72176665..bca0d4e62a4 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -915,6 +915,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) break; case NotifyUngrab: keyboard_grabbed = FALSE; + retry_grab_clipping_window(); break; } @@ -926,12 +927,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) return FALSE; } - /* ask the foreground window to re-apply the current ClipCursor rect */ - if (!send_message_timeout( NtUserGetForegroundWindow(), WM_X11DRV_CLIP_CURSOR_REQUEST, 0, 0, - SMTO_NOTIMEOUTIFNOTHUNG, 500, NULL ) && - RtlGetLastWin32Error() == ERROR_TIMEOUT) - ERR( "WM_X11DRV_CLIP_CURSOR_REQUEST timed out.\n" ); - /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; @@ -1057,6 +1052,12 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) break; case NotifyGrab: keyboard_grabbed = TRUE; + + /* This will do nothing due to keyboard_grabbed == TRUE, but it + * will save the current clipping rect so we can restore it on + * FocusIn with NotifyUngrab mode. + */ + retry_grab_clipping_window(); break; } diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 0e4a87e6cd6..90ecd457f34 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -129,6 +129,9 @@ XContext cursor_context = 0; static HWND cursor_window; static HCURSOR last_cursor; static DWORD last_cursor_change; +static RECT last_clip_rect; +static HWND last_clip_foreground_window; +static BOOL last_clip_refused; static RECT clip_rect; static Cursor create_cursor( HANDLE handle ); @@ -459,8 +462,15 @@ static BOOL grab_clipping_window( const RECT *clip ) if (keyboard_grabbed) { WARN( "refusing to clip to %s\n", wine_dbgstr_rect(clip) ); + last_clip_refused = TRUE; + last_clip_foreground_window = NtUserGetForegroundWindow(); + last_clip_rect = *clip; return FALSE; } + else + { + last_clip_refused = FALSE; + } /* enable XInput2 unless we are already clipping */ if (!data->clip_hwnd) X11DRV_XInput2_Enable( data->display, None, PointerMotionMask ); @@ -543,6 +553,20 @@ void reset_clipping_window(void) NtUserClipCursor( NULL ); /* make sure the clip rectangle is reset too */ } +/*********************************************************************** + * retry_grab_clipping_window + * + * Restore the current clip rectangle or retry the last one if it has + * been refused because of an active keyboard grab. + */ +void retry_grab_clipping_window(void) +{ + if (clipping_cursor) + NtUserClipCursor( &clip_rect ); + else if (last_clip_refused && NtUserGetForegroundWindow() == last_clip_foreground_window) + NtUserClipCursor( &last_clip_rect ); +} + /*********************************************************************** * clip_cursor_notify * diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 1d4e2f75486..b601ab6880e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -742,6 +742,7 @@ extern LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip extern LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) DECLSPEC_HIDDEN; extern void ungrab_clipping_window(void) DECLSPEC_HIDDEN; extern void reset_clipping_window(void) DECLSPEC_HIDDEN; +extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; extern void X11DRV_InitKeyboard( Display *display ) DECLSPEC_HIDDEN; From b68bb5817f76c0183687ddaafeb4494d896c1c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:23 +0200 Subject: [PATCH 472/758] Revert "winex11.drv: Release pointer grab on FocusOut events." This reverts commit 0227566268174b62cd0a6dbe97fdb25b1a398e17. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index bca0d4e62a4..ec585c17a11 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1061,8 +1061,6 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) break; } - if (hwnd == NtUserGetForegroundWindow()) ungrab_clipping_window(); - /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; From f5724066d21c924279dba77f2176daa477f7888a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:24 +0200 Subject: [PATCH 473/758] Revert "winex11.drv: Release pointer grab on focus change." This reverts commit bf0570916cb297aebe9a1361bf0c63d4f378dca0. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 2 -- dlls/winex11.drv/window.c | 2 -- dlls/winex11.drv/x11drv.h | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index ec585c17a11..b3073fc4423 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -706,8 +706,6 @@ static void set_focus( XEvent *xev, HWND hwnd, Time time ) if (!try_grab_pointer( xev->xany.display )) { - /* ask the foreground window to release its grab before trying to get ours */ - send_message( NtUserGetForegroundWindow(), WM_X11DRV_RELEASE_CURSOR, 0, 0 ); XSendEvent( xev->xany.display, xev->xany.window, False, 0, xev ); return; } diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 08656010c2f..d8bb0f2effd 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3732,8 +3732,6 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) return 0; case WM_X11DRV_ADD_TAB: taskbar_add_tab( hwnd ); - case WM_X11DRV_RELEASE_CURSOR: - ungrab_clipping_window(); return 0; default: FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, (long)wp, lp ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index b601ab6880e..b082ee6a219 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -611,8 +611,7 @@ enum x11drv_window_messages WM_X11DRV_CLIP_CURSOR_NOTIFY, WM_X11DRV_CLIP_CURSOR_REQUEST, WM_X11DRV_DELETE_TAB, - WM_X11DRV_ADD_TAB, - WM_X11DRV_RELEASE_CURSOR, + WM_X11DRV_ADD_TAB }; /* _NET_WM_STATE properties that we keep track of */ From ea7d6adc5fdb58e0bb91c6b0b56e99687b0b227b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:24 +0200 Subject: [PATCH 474/758] Revert "winex11.drv: Wait for pointer grab on FocusIn/WM_TAKE_FOCUS events." This reverts commit 14ad9a3418d8ac76cc956cd6878a4b393dc48937. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 43 +++------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index b3073fc4423..98ecf4b8ad9 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -386,25 +386,6 @@ static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawE } #endif -static int try_grab_pointer( Display *display ) -{ - if (!grab_pointer) - return 1; - - /* if we are already clipping the cursor in the current thread, we should not - * call XGrabPointer here or it would change the confine-to window. */ - if (clipping_cursor && x11drv_thread_data()->clip_hwnd) - return 1; - - if (XGrabPointer( display, root_window, False, 0, GrabModeAsync, GrabModeAsync, - None, None, CurrentTime ) != GrabSuccess) - return 0; - - XUngrabPointer( display, CurrentTime ); - XFlush( display ); - return 1; -} - /*********************************************************************** * merge_events * @@ -704,16 +685,8 @@ static void set_focus( XEvent *xev, HWND hwnd, Time time ) Window win; GUITHREADINFO threadinfo; - if (!try_grab_pointer( xev->xany.display )) - { - XSendEvent( xev->xany.display, xev->xany.window, False, 0, xev ); - return; - } - else - { - TRACE( "setting foreground window to %p\n", hwnd ); - NtUserSetForegroundWindow( hwnd ); - } + TRACE( "setting foreground window to %p\n", hwnd ); + NtUserSetForegroundWindow( hwnd ); threadinfo.cbSize = sizeof(threadinfo); NtUserGetGUIThreadInfo( 0, &threadinfo ); @@ -917,14 +890,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) break; } - if (!try_grab_pointer( event->display )) - { - /* ask the desktop window to release its grab before trying to get ours */ - send_message( NtUserGetDesktopWindow(), WM_X11DRV_RELEASE_CURSOR, 0, 0 ); - XSendEvent( event->display, event->window, False, 0, xev ); - return FALSE; - } - /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; @@ -943,10 +908,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (!hwnd) hwnd = get_active_window(); if (!hwnd) hwnd = x11drv_thread_data()->last_focus; if (hwnd && can_activate_window(hwnd)) set_focus( xev, hwnd, CurrentTime ); - return TRUE; } - - NtUserSetForegroundWindow( hwnd ); + else NtUserSetForegroundWindow( hwnd ); return TRUE; } From c7b205db96c9a9e147ddc300909df8a72ffa3ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:19:37 +0200 Subject: [PATCH 475/758] Revert "winex11.drv: Merge FocusIn/FocusOut NotifyGrab/NotifyUngrab cases." This reverts commit 0fc22ab365fd4c5728d9f51d4d844afb5b85aa6a. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 98ecf4b8ad9..85d655a5251 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -877,7 +877,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) case NotifyGrab: /* these are received when moving undecorated managed windows on mutter */ keyboard_grabbed = TRUE; - break; + return FALSE; case NotifyWhileGrabbed: keyboard_grabbed = TRUE; break; @@ -887,14 +887,10 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) case NotifyUngrab: keyboard_grabbed = FALSE; retry_grab_clipping_window(); - break; + return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ } - /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; - xim_set_focus( hwnd, TRUE ); - if (use_take_focus) { if (hwnd == NtUserGetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE ); @@ -1004,7 +1000,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) case NotifyUngrab: /* these are received when moving undecorated managed windows on mutter */ keyboard_grabbed = FALSE; - break; + return FALSE; case NotifyNormal: keyboard_grabbed = FALSE; break; @@ -1019,11 +1015,9 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) * FocusIn with NotifyUngrab mode. */ retry_grab_clipping_window(); - break; - } - /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; + return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ + } focus_out( event->display, hwnd ); return TRUE; From 2eae807b92652f290dcc53e9f7b415045209430d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 5 Jun 2023 10:44:28 +0200 Subject: [PATCH 476/758] Revert "winex11.drv: Pass XEvent instead of Display to set_focus." This reverts commit 0ff06a59a96d89b1dcb203f340a871f1a0885a57. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 85d655a5251..72eb2cfe443 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -679,7 +679,7 @@ static void set_input_focus( struct x11drv_win_data *data ) /********************************************************************** * set_focus */ -static void set_focus( XEvent *xev, HWND hwnd, Time time ) +static void set_focus( Display *display, HWND hwnd, Time time ) { HWND focus; Window win; @@ -698,7 +698,7 @@ static void set_focus( XEvent *xev, HWND hwnd, Time time ) if (win) { TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time ); - XSetInputFocus( xev->xany.display, win, RevertToParent, time ); + XSetInputFocus( display, win, RevertToParent, time ); } } @@ -807,7 +807,7 @@ static void handle_wm_protocols( HWND hwnd, XEvent *xev ) MAKELONG( HTMENU, WM_LBUTTONDOWN ) ); if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) { - set_focus( xev, hwnd, event_time ); + set_focus( event->display, hwnd, event_time ); return; } } @@ -816,7 +816,7 @@ static void handle_wm_protocols( HWND hwnd, XEvent *xev ) hwnd = NtUserGetForegroundWindow(); if (!hwnd) hwnd = last_focus; if (!hwnd) hwnd = NtUserGetDesktopWindow(); - set_focus( xev, hwnd, event_time ); + set_focus( event->display, hwnd, event_time ); return; } /* try to find some other window to give the focus to */ @@ -824,7 +824,7 @@ static void handle_wm_protocols( HWND hwnd, XEvent *xev ) if (hwnd) hwnd = NtUserGetAncestor( hwnd, GA_ROOT ); if (!hwnd) hwnd = get_active_window(); if (!hwnd) hwnd = last_focus; - if (hwnd && can_activate_window(hwnd)) set_focus( xev, hwnd, event_time ); + if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time ); } else if (protocol == x11drv_atom(_NET_WM_PING)) { @@ -903,7 +903,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (hwnd) hwnd = NtUserGetAncestor( hwnd, GA_ROOT ); if (!hwnd) hwnd = get_active_window(); if (!hwnd) hwnd = x11drv_thread_data()->last_focus; - if (hwnd && can_activate_window(hwnd)) set_focus( xev, hwnd, CurrentTime ); + if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime ); } else NtUserSetForegroundWindow( hwnd ); return TRUE; From 24a5e666f77b86a1e69347c9330a7eb97a4f5722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:20:50 +0200 Subject: [PATCH 477/758] Revert "winex11.drv: Ignore ClipCursor if desktop window is foreground." This reverts commit 5d75cc698669673ab44e65a3181f93d6a99512af. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 90ecd457f34..eebfb4939a8 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1667,13 +1667,6 @@ BOOL X11DRV_ClipCursor( LPCRECT clip ) HWND foreground = NtUserGetForegroundWindow(); DWORD tid, pid; - if (foreground == NtUserGetDesktopWindow()) - { - WARN( "desktop is foreground, ignoring ClipCursor\n" ); - ungrab_clipping_window(); - return TRUE; - } - /* forward request to the foreground window if it's in a different thread */ tid = NtUserGetWindowThread( foreground, &pid ); if (tid && tid != GetCurrentThreadId() && pid == GetCurrentProcessId()) From 56c7454a19653495f535ce115a8061441e6c5cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:21:35 +0200 Subject: [PATCH 478/758] Revert "server: Use the helper to reset the clip rect when the desktop size changes." This reverts commit 2a44dd0e99fb87b86441e43a181251a0f061a59c. CW-Bug-Id: #21879 --- server/queue.c | 2 +- server/user.h | 1 - server/window.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/server/queue.c b/server/queue.c index f51263ed692..d1ebe48958d 100644 --- a/server/queue.c +++ b/server/queue.c @@ -538,7 +538,7 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig } /* set the cursor clip rectangle */ -void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ) +static void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ) { rectangle_t top_rect, new_rect; int x, y; diff --git a/server/user.h b/server/user.h index deebd92ee6a..bbc665b1da1 100644 --- a/server/user.h +++ b/server/user.h @@ -117,7 +117,6 @@ extern void post_win_event( struct thread *thread, unsigned int event, user_handle_t handle ); extern void free_hotkeys( struct desktop *desktop, user_handle_t window ); extern void free_touches( struct desktop *desktop, user_handle_t window ); -extern void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ); /* region functions */ diff --git a/server/window.c b/server/window.c index 302aa2e6071..83aab43a583 100644 --- a/server/window.c +++ b/server/window.c @@ -1831,7 +1831,7 @@ static void set_window_pos( struct window *win, struct window *previous, } /* reset cursor clip rectangle when the desktop changes size */ - if (win == win->desktop->top_window) set_clip_rectangle( win->desktop, NULL, 0 ); + if (win == win->desktop->top_window) win->desktop->shared->cursor.clip = *window_rect; /* if the window is not visible, everything is easy */ if (!visible) return; From 39179a21160c3dd9a16709d83108097d0cefa746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:39:26 +0200 Subject: [PATCH 479/758] Revert "winex11.drv: Enable fullscreen clipping even if not already clipping." This reverts commit dba0a0db9bb647ab8d0d5b894d9e8cc00ea181b5. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index eebfb4939a8..4d475173347 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1682,12 +1682,12 @@ BOOL X11DRV_ClipCursor( LPCRECT clip ) { if (grab_clipping_window( clip )) return TRUE; } - else /* check if we should switch to fullscreen clipping */ + else /* if currently clipping, check if we should switch to fullscreen clipping */ { struct x11drv_thread_data *data = x11drv_thread_data(); - if (data) + if (data && data->clip_hwnd) { - if ((data->clip_hwnd && EqualRect( clip, &clip_rect ) && !EqualRect(&clip_rect, &virtual_rect)) || clip_fullscreen_window( foreground, TRUE )) + if (EqualRect( clip, &clip_rect ) || clip_fullscreen_window( foreground, TRUE )) return TRUE; } } From 5eb8d445d69adc233b2a7af430d4c09c25ac2949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 11:43:09 +0200 Subject: [PATCH 480/758] Revert "winex11.drv: Ignore clip_reset when trying to clip the mouse after the desktop has been resized." This reverts commit e56ad03113d0a67024b6e207af27cc434dc85b1f. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 4d475173347..a4a3715994f 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -629,10 +629,9 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) release_win_data( data ); if (!fullscreen) return FALSE; if (!(thread_data = x11drv_thread_data())) return FALSE; - if (!reset) { - if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; - if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ - } + if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; + if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ + monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ); if (!monitor) return FALSE; monitor_info.cbSize = sizeof(monitor_info); From c6441b2a0ee6159350abb05f73040395252ec04f Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 28 Feb 2023 09:49:36 -0700 Subject: [PATCH 481/758] winex11: Avoid calling RtlInitUnicodeString on a static constant. (cherry picked from commit 354e999c5a8f284af3fe833f9fb45bace151a44e) --- dlls/winex11.drv/mouse.c | 3 +-- dlls/winex11.drv/window.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index a4a3715994f..faa3aacdd3f 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -441,7 +441,7 @@ static BOOL grab_clipping_window( const RECT *clip ) #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0}; struct x11drv_thread_data *data = x11drv_thread_data(); - UNICODE_STRING class_name; + UNICODE_STRING class_name = RTL_CONSTANT_STRING( messageW ); Window clip_window; HWND msg_hwnd = 0; POINT pos; @@ -453,7 +453,6 @@ static BOOL grab_clipping_window( const RECT *clip ) if (!data) return FALSE; if (!(clip_window = init_clip_window())) return TRUE; - RtlInitUnicodeString( &class_name, messageW ); if (!(msg_hwnd = NtUserCreateWindowEx( 0, &class_name, &class_name, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, NtCurrentTeb()->Peb->ImageBaseAddress, NULL, 0, NULL, 0, FALSE ))) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index d8bb0f2effd..6580a2f016e 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2511,7 +2511,7 @@ HWND create_foreign_window( Display *display, Window xwin ) unsigned int nchildren; XWindowAttributes attr; UINT style = WS_CLIPCHILDREN; - UNICODE_STRING class_name; + UNICODE_STRING class_name = RTL_CONSTANT_STRING( classW ); if (!class_registered) { @@ -2522,7 +2522,6 @@ HWND create_foreign_window( Display *display, Window xwin ) class.cbSize = sizeof(class); class.lpfnWndProc = client_foreign_window_proc; class.lpszClassName = classW; - RtlInitUnicodeString( &class_name, classW ); if (!NtUserRegisterClassExWOW( &class, &class_name, &version, NULL, 0, 0, NULL ) && RtlGetLastWin32Error() != ERROR_CLASS_ALREADY_EXISTS) { From 5bada66bcf4f3043c04c66b23547f1a9816780df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 May 2023 23:39:59 +0200 Subject: [PATCH 482/758] server: Assume the internal clip message to be WM_WINE_CLIPCURSOR. (cherry picked from commit 61dbfea452c0efc676d521ab5374abda108f4f1e) CW-Bug-Id: #21879 --- dlls/win32u/cursoricon.c | 1 - include/wine/server_protocol.h | 2 -- server/protocol.def | 1 - server/queue.c | 9 ++------- server/request.h | 3 +-- server/trace.c | 1 - server/user.h | 1 - server/winstation.c | 1 - 8 files changed, 3 insertions(+), 16 deletions(-) diff --git a/dlls/win32u/cursoricon.c b/dlls/win32u/cursoricon.c index eb1d07afb6c..ec143b841fd 100644 --- a/dlls/win32u/cursoricon.c +++ b/dlls/win32u/cursoricon.c @@ -174,7 +174,6 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) SERVER_START_REQ( set_cursor ) { - req->clip_msg = WM_WINE_CLIPCURSOR; if (rect) { req->flags = SET_CURSOR_CLIP; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 472c0ea709d..7a7b22c0883 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5281,8 +5281,6 @@ struct set_cursor_request int x; int y; rectangle_t clip; - unsigned int clip_msg; - char __pad_52[4]; }; struct set_cursor_reply { diff --git a/server/protocol.def b/server/protocol.def index 9589aacfbbc..9770db77706 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3749,7 +3749,6 @@ struct handle_info int x; /* cursor position */ int y; rectangle_t clip; /* cursor clip rectangle */ - unsigned int clip_msg; /* message to post on cursor clip changes */ @REPLY user_handle_t prev_handle; /* previous handle */ int prev_count; /* previous show count */ diff --git a/server/queue.c b/server/queue.c index d1ebe48958d..94e8aa5448e 100644 --- a/server/queue.c +++ b/server/queue.c @@ -34,6 +34,7 @@ #include "wingdi.h" #include "winuser.h" #include "winternl.h" +#include "ntuser.h" #include "handle.h" #include "file.h" @@ -558,8 +559,7 @@ static void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect SHARED_WRITE_BEGIN( &desktop->shared->seq ); desktop->shared->cursor.clip = new_rect; - if (desktop->cursor_clip_msg && send_clip_msg) - post_desktop_message( desktop, desktop->cursor_clip_msg, rect != NULL, 0 ); + if (send_clip_msg) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, rect != NULL, 0 ); /* warp the mouse to be inside the clip rect */ x = max( min( desktop->shared->cursor.x, desktop->shared->cursor.clip.right - 1 ), desktop->shared->cursor.clip.left ); @@ -3718,11 +3718,6 @@ DECL_HANDLER(set_cursor) if (req->flags & (SET_CURSOR_CLIP | SET_CURSOR_NOCLIP)) { struct desktop *desktop = input->desktop; - - /* only the desktop owner can set the message */ - if (req->clip_msg && get_top_window_owner(desktop) == current->process) - desktop->cursor_clip_msg = req->clip_msg; - set_clip_rectangle( desktop, (req->flags & SET_CURSOR_NOCLIP) ? NULL : &req->clip, 0 ); } diff --git a/server/request.h b/server/request.h index 089af79e199..3443ee93c17 100644 --- a/server/request.h +++ b/server/request.h @@ -2209,8 +2209,7 @@ C_ASSERT( FIELD_OFFSET(struct set_cursor_request, show_count) == 20 ); C_ASSERT( FIELD_OFFSET(struct set_cursor_request, x) == 24 ); C_ASSERT( FIELD_OFFSET(struct set_cursor_request, y) == 28 ); C_ASSERT( FIELD_OFFSET(struct set_cursor_request, clip) == 32 ); -C_ASSERT( FIELD_OFFSET(struct set_cursor_request, clip_msg) == 48 ); -C_ASSERT( sizeof(struct set_cursor_request) == 56 ); +C_ASSERT( sizeof(struct set_cursor_request) == 48 ); C_ASSERT( FIELD_OFFSET(struct set_cursor_reply, prev_handle) == 8 ); C_ASSERT( FIELD_OFFSET(struct set_cursor_reply, prev_count) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_cursor_reply, prev_x) == 16 ); diff --git a/server/trace.c b/server/trace.c index b749c54a900..a360b4706ce 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4398,7 +4398,6 @@ static void dump_set_cursor_request( const struct set_cursor_request *req ) fprintf( stderr, ", x=%d", req->x ); fprintf( stderr, ", y=%d", req->y ); dump_rectangle( ", clip=", &req->clip ); - fprintf( stderr, ", clip_msg=%08x", req->clip_msg ); } static void dump_set_cursor_reply( const struct set_cursor_reply *req ) diff --git a/server/user.h b/server/user.h index bbc665b1da1..c8dc6e922d8 100644 --- a/server/user.h +++ b/server/user.h @@ -68,7 +68,6 @@ struct desktop struct thread_input *foreground_input; /* thread input of foreground thread */ unsigned int users; /* processes and threads using this desktop */ unsigned char keystate[256]; /* asynchronous key state */ - unsigned int cursor_clip_msg; /* message to post for cursor clip changes */ user_handle_t cursor_win; /* window that contains the cursor */ struct object *shared_mapping; /* desktop shared memory mapping */ volatile struct desktop_shared_memory *shared; /* desktop shared memory ptr */ diff --git a/server/winstation.c b/server/winstation.c index 53613592a82..9e5d3871a6f 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -260,7 +260,6 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->close_timeout_val = 0; desktop->foreground_input = NULL; desktop->users = 0; - desktop->cursor_clip_msg = 0; desktop->cursor_win = 0; desktop->last_press_alt = 0; list_add_tail( &winstation->desktops, &desktop->entry ); From 4fbc55aa36f23c400f842c86cfa9be3da538b0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Jan 2021 15:05:19 +0100 Subject: [PATCH 483/758] server: Move set_cursor desktop local variable to wider scope. (cherry picked from commit 26c6386de90b93503231dcf44dc4efee28bbd736) CW-Bug-Id: #21879 --- server/queue.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/server/queue.c b/server/queue.c index 94e8aa5448e..b1c2bc65218 100644 --- a/server/queue.c +++ b/server/queue.c @@ -3684,14 +3684,16 @@ DECL_HANDLER(set_cursor) { struct msg_queue *queue = get_current_queue(); struct thread_input *input; + struct desktop *desktop; if (!queue) return; input = queue->input; + desktop = input->desktop; reply->prev_handle = input->shared->cursor; reply->prev_count = input->shared->cursor_count; - reply->prev_x = input->desktop->shared->cursor.x; - reply->prev_y = input->desktop->shared->cursor.y; + reply->prev_x = desktop->shared->cursor.x; + reply->prev_y = desktop->shared->cursor.y; if ((req->flags & SET_CURSOR_HANDLE) && req->handle && !get_user_object( req->handle, USER_CLIENT )) @@ -3711,20 +3713,14 @@ DECL_HANDLER(set_cursor) input->shared->cursor_count += req->show_count; } SHARED_WRITE_END( &input->shared->seq ); - if (req->flags & SET_CURSOR_POS) - { - set_cursor_pos( input->desktop, req->x, req->y ); - } - if (req->flags & (SET_CURSOR_CLIP | SET_CURSOR_NOCLIP)) - { - struct desktop *desktop = input->desktop; - set_clip_rectangle( desktop, (req->flags & SET_CURSOR_NOCLIP) ? NULL : &req->clip, 0 ); - } - - reply->new_x = input->desktop->shared->cursor.x; - reply->new_y = input->desktop->shared->cursor.y; - reply->new_clip = input->desktop->shared->cursor.clip; - reply->last_change = input->desktop->shared->cursor.last_change; + if (req->flags & SET_CURSOR_POS) set_cursor_pos( desktop, req->x, req->y ); + if (req->flags & SET_CURSOR_CLIP) set_clip_rectangle( desktop, &req->clip, 0 ); + if (req->flags & SET_CURSOR_NOCLIP) set_clip_rectangle( desktop, NULL, 0 ); + + reply->new_x = desktop->shared->cursor.x; + reply->new_y = desktop->shared->cursor.y; + reply->new_clip = desktop->shared->cursor.clip; + reply->last_change = desktop->shared->cursor.last_change; } /* Get the history of the 64 last cursor positions */ From b85871b9f8444faa5a32d894c2a92ac1bd28b765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 13:50:11 +0200 Subject: [PATCH 484/758] win32u: Rename user driver CreateDesktopWindow entry to SetDesktopWindow. This doesn't create anything, but instead notifies the user driver of the current desktop window, either when it is created, or when a thread calling NtUserGetDesktopWindow receives the current desktop window. (cherry picked from commit 15dfe2ed2d625d0a80c6189472285af67a5f252d) CW-Bug-Id: #21879 --- dlls/win32u/driver.c | 23 +++++++++++------------ dlls/win32u/window.c | 4 +--- dlls/win32u/winstation.c | 5 ++--- dlls/winemac.drv/gdi.c | 2 +- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/window.c | 5 ++--- dlls/winex11.drv/init.c | 2 +- dlls/winex11.drv/window.c | 5 ++--- dlls/winex11.drv/x11drv.h | 2 +- include/wine/gdi_driver.h | 2 +- 10 files changed, 23 insertions(+), 29 deletions(-) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 5b8cbb56813..2d5d987652c 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -783,11 +783,6 @@ static BOOL nulldrv_UpdateDisplayDevices( const struct gdi_device_manager *manag return FALSE; } -static BOOL nulldrv_CreateDesktopWindow( HWND hwnd ) -{ - return TRUE; -} - static BOOL nodrv_CreateWindow( HWND hwnd ) { static int warned; @@ -842,6 +837,10 @@ static void nulldrv_SetCapture( HWND hwnd, UINT flags ) { } +static void nulldrv_SetDesktopWindow( HWND hwnd ) +{ +} + static void nulldrv_SetFocus( HWND hwnd ) { } @@ -1150,11 +1149,6 @@ static BOOL loaderdrv_UpdateDisplayDevices( const struct gdi_device_manager *man return load_driver()->pUpdateDisplayDevices( manager, force, param ); } -static BOOL loaderdrv_CreateDesktopWindow( HWND hwnd ) -{ - return load_driver()->pCreateDesktopWindow( hwnd ); -} - static BOOL loaderdrv_CreateWindow( HWND hwnd ) { return load_driver()->pCreateWindow( hwnd ); @@ -1171,6 +1165,11 @@ static void loaderdrv_FlashWindowEx( FLASHWINFO *info ) load_driver()->pFlashWindowEx( info ); } +static void loaderdrv_SetDesktopWindow( HWND hwnd ) +{ + load_driver()->pSetDesktopWindow( hwnd ); +} + static void loaderdrv_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags ) { load_driver()->pSetLayeredWindowAttributes( hwnd, key, alpha, flags ); @@ -1223,7 +1222,6 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_GetDisplayDepth, loaderdrv_UpdateDisplayDevices, /* windowing functions */ - loaderdrv_CreateDesktopWindow, loaderdrv_CreateWindow, nulldrv_DesktopWindowProc, nulldrv_DestroyWindow, @@ -1233,6 +1231,7 @@ static const struct user_driver_funcs lazy_load_driver = nulldrv_ReleaseDC, nulldrv_ScrollDC, nulldrv_SetCapture, + loaderdrv_SetDesktopWindow, nulldrv_SetFocus, loaderdrv_SetLayeredWindowAttributes, nulldrv_SetParent, @@ -1301,7 +1300,6 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(GetCurrentDisplaySettings); SET_USER_FUNC(GetDisplayDepth); SET_USER_FUNC(UpdateDisplayDevices); - SET_USER_FUNC(CreateDesktopWindow); SET_USER_FUNC(CreateWindow); SET_USER_FUNC(DesktopWindowProc); SET_USER_FUNC(DestroyWindow); @@ -1311,6 +1309,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(ReleaseDC); SET_USER_FUNC(ScrollDC); SET_USER_FUNC(SetCapture); + SET_USER_FUNC(SetDesktopWindow); SET_USER_FUNC(SetFocus); SET_USER_FUNC(SetLayeredWindowAttributes); SET_USER_FUNC(SetParent); diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 333fde4b472..eb9a71fa86f 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4972,9 +4972,7 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name, if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle ); else assert( full_parent == UlongToHandle( thread_info->top_window )); - if (full_parent && - !user_driver->pCreateDesktopWindow( UlongToHandle( thread_info->top_window ))) - ERR( "failed to create desktop window\n" ); + if (full_parent) user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window )); register_builtin_classes(); } else /* HWND_MESSAGE parent */ diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 79808010598..79dfa4671ac 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -520,9 +520,8 @@ HWND get_desktop_window(void) SERVER_END_REQ; } - if (!thread_info->top_window || - !user_driver->pCreateDesktopWindow( UlongToHandle( thread_info->top_window ))) - ERR_(win)( "failed to create desktop window\n" ); + if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" ); + else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window )); register_builtin_classes(); return UlongToHandle( thread_info->top_window ); diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index ef19b39cade..4d2f25983f8 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -271,7 +271,6 @@ static const struct user_driver_funcs macdrv_funcs = .pChangeDisplaySettings = macdrv_ChangeDisplaySettings, .pClipCursor = macdrv_ClipCursor, .pClipboardWindowProc = macdrv_ClipboardWindowProc, - .pCreateDesktopWindow = macdrv_CreateDesktopWindow, .pDesktopWindowProc = macdrv_DesktopWindowProc, .pDestroyCursorIcon = macdrv_DestroyCursorIcon, .pDestroyWindow = macdrv_DestroyWindow, @@ -287,6 +286,7 @@ static const struct user_driver_funcs macdrv_funcs = .pSetCapture = macdrv_SetCapture, .pSetCursor = macdrv_SetCursor, .pSetCursorPos = macdrv_SetCursorPos, + .pSetDesktopWindow = macdrv_SetDesktopWindow, .pSetFocus = macdrv_SetFocus, .pSetLayeredWindowAttributes = macdrv_SetLayeredWindowAttributes, .pSetParent = macdrv_SetParent, diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 3dedd5e3043..d0fddcc0f21 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -134,9 +134,9 @@ extern BOOL macdrv_UpdateDisplayDevices( const struct gdi_device_manager *device extern BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp) DECLSPEC_HIDDEN; extern BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp) DECLSPEC_HIDDEN; extern BOOL macdrv_ClipCursor(LPCRECT clip) DECLSPEC_HIDDEN; -extern BOOL macdrv_CreateDesktopWindow(HWND hwnd) DECLSPEC_HIDDEN; extern LRESULT macdrv_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) DECLSPEC_HIDDEN; extern void macdrv_DestroyWindow(HWND hwnd) DECLSPEC_HIDDEN; +extern void macdrv_SetDesktopWindow(HWND hwnd) DECLSPEC_HIDDEN; extern void macdrv_SetFocus(HWND hwnd) DECLSPEC_HIDDEN; extern void macdrv_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWORD flags) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 896efb6a68e..5b053f379a5 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -1528,9 +1528,9 @@ static void perform_window_command(HWND hwnd, unsigned int style_any, unsigned i /********************************************************************** - * CreateDesktopWindow (MACDRV.@) + * SetDesktopWindow (MACDRV.@) */ -BOOL macdrv_CreateDesktopWindow(HWND hwnd) +void macdrv_SetDesktopWindow(HWND hwnd) { unsigned int width, height; @@ -1567,7 +1567,6 @@ BOOL macdrv_CreateDesktopWindow(HWND hwnd) } set_app_icon(); - return TRUE; } void macdrv_resize_desktop(void) diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index fb879c9b483..9b457b6ccd8 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -411,7 +411,6 @@ static const struct user_driver_funcs x11drv_funcs = .pGetCurrentDisplaySettings = X11DRV_GetCurrentDisplaySettings, .pGetDisplayDepth = X11DRV_GetDisplayDepth, .pUpdateDisplayDevices = X11DRV_UpdateDisplayDevices, - .pCreateDesktopWindow = X11DRV_CreateDesktopWindow, .pCreateWindow = X11DRV_CreateWindow, .pDesktopWindowProc = X11DRV_DesktopWindowProc, .pDestroyWindow = X11DRV_DestroyWindow, @@ -421,6 +420,7 @@ static const struct user_driver_funcs x11drv_funcs = .pReleaseDC = X11DRV_ReleaseDC, .pScrollDC = X11DRV_ScrollDC, .pSetCapture = X11DRV_SetCapture, + .pSetDesktopWindow = X11DRV_SetDesktopWindow, .pSetFocus = X11DRV_SetFocus, .pSetLayeredWindowAttributes = X11DRV_SetLayeredWindowAttributes, .pSetParent = X11DRV_SetParent, diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 6580a2f016e..9c030f61f14 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2312,9 +2312,9 @@ BOOL create_desktop_win_data( Window win ) } /********************************************************************** - * CreateDesktopWindow (X11DRV.@) + * SetDesktopWindow (X11DRV.@) */ -BOOL X11DRV_CreateDesktopWindow( HWND hwnd ) +void X11DRV_SetDesktopWindow( HWND hwnd ) { unsigned int width, height; @@ -2354,7 +2354,6 @@ BOOL X11DRV_CreateDesktopWindow( HWND hwnd ) Window win = (Window)NtUserGetProp( hwnd, whole_window_prop ); if (win && win != root_window) X11DRV_init_desktop( win, width, height ); } - return TRUE; } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index b082ee6a219..089fd849cb9 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -224,7 +224,6 @@ extern BOOL X11DRV_GetCurrentDisplaySettings( LPCWSTR name, BOOL is_primary, LPD extern INT X11DRV_GetDisplayDepth( LPCWSTR name, BOOL is_primary ) DECLSPEC_HIDDEN; extern BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param ) DECLSPEC_HIDDEN; -extern BOOL X11DRV_CreateDesktopWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL X11DRV_CreateWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern LRESULT X11DRV_DesktopWindowProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) DECLSPEC_HIDDEN; extern void X11DRV_DestroyWindow( HWND hwnd ) DECLSPEC_HIDDEN; @@ -234,6 +233,7 @@ extern void X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect, extern void X11DRV_ReleaseDC( HWND hwnd, HDC hdc ) DECLSPEC_HIDDEN; extern BOOL X11DRV_ScrollDC( HDC hdc, INT dx, INT dy, HRGN update ) DECLSPEC_HIDDEN; extern void X11DRV_SetCapture( HWND hwnd, UINT flags ) DECLSPEC_HIDDEN; +extern void X11DRV_SetDesktopWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern void X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags ) DECLSPEC_HIDDEN; extern void X11DRV_SetParent( HWND hwnd, HWND parent, HWND old_parent ) DECLSPEC_HIDDEN; diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index c73b29015fc..f94dc588fc8 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -306,7 +306,6 @@ struct user_driver_funcs INT (*pGetDisplayDepth)(LPCWSTR,BOOL); BOOL (*pUpdateDisplayDevices)(const struct gdi_device_manager *,BOOL,void*); /* windowing functions */ - BOOL (*pCreateDesktopWindow)(HWND); BOOL (*pCreateWindow)(HWND); LRESULT (*pDesktopWindowProc)(HWND,UINT,WPARAM,LPARAM); void (*pDestroyWindow)(HWND); @@ -316,6 +315,7 @@ struct user_driver_funcs void (*pReleaseDC)(HWND,HDC); BOOL (*pScrollDC)(HDC,INT,INT,HRGN); void (*pSetCapture)(HWND,UINT); + void (*pSetDesktopWindow)(HWND); void (*pSetFocus)(HWND); void (*pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD); void (*pSetParent)(HWND,HWND,HWND); From 2c0e13ff2380af9bf1fd139e1173b145da672312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 13:52:16 +0200 Subject: [PATCH 485/758] win32u: Call SetDesktopWindow when desktop window is successfully created. When the default desktop window is created, its parent is always NULL, and SetDesktopWindow is never called here. (cherry picked from commit 8b5bdb10d586d069e3ed5bc5138015f742a9183b) CW-Bug-Id: #21879 --- dlls/win32u/window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index eb9a71fa86f..95569ba187a 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4969,10 +4969,10 @@ static WND *create_window_handle( HWND parent, HWND owner, UNICODE_STRING *name, if (name->Buffer == (const WCHAR *)DESKTOP_CLASS_ATOM) { - if (!thread_info->top_window) - thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle ); + if (!thread_info->top_window) thread_info->top_window = HandleToUlong( full_parent ? full_parent : handle ); else assert( full_parent == UlongToHandle( thread_info->top_window )); - if (full_parent) user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window )); + if (!thread_info->top_window) ERR_(win)( "failed to create desktop window\n" ); + else user_driver->pSetDesktopWindow( UlongToHandle( thread_info->top_window )); register_builtin_classes(); } else /* HWND_MESSAGE parent */ From 63ab44050c809d9b394bd57efae2bf71f8c02540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 16:18:17 +0200 Subject: [PATCH 486/758] explorer: Load graphics driver before calling CreateDesktopW. (cherry picked from commit f9c1b6af126a948750adb658248141a8f0178f46) CW-Bug-Id: #21879 --- programs/explorer/desktop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 14b2cbc2aff..8ac4a5e03c6 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -1114,6 +1114,10 @@ void manage_desktop( WCHAR *arg ) if (name) enable_shell = get_default_enable_shell( name ); + UuidCreate( &guid ); + TRACE( "display guid %s\n", debugstr_guid(&guid) ); + graphics_driver = load_graphics_driver( driver, &guid ); + if (name && width && height) { if (!(desktop = CreateDesktopW( name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ))) @@ -1124,10 +1128,6 @@ void manage_desktop( WCHAR *arg ) SetThreadDesktop( desktop ); } - UuidCreate( &guid ); - TRACE( "display guid %s\n", debugstr_guid(&guid) ); - graphics_driver = load_graphics_driver( driver, &guid ); - /* create the desktop window */ hwnd = CreateWindowExW( 0, DESKTOP_CLASS_ATOM, NULL, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 0, 0, 0, 0, 0, &guid ); From 87660c18c81e1aa6fbba05b6332be23317969cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 17:33:03 +0200 Subject: [PATCH 487/758] explorer: Use root window if driver doesn't implement create_desktop. (cherry picked from commit dbb63987f0b6ed821d872fe44eed7eebd0e7fda2) CW-Bug-Id: #21879 --- programs/explorer/desktop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 8ac4a5e03c6..11f81b1d800 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -773,7 +773,7 @@ static LRESULT WINAPI desktop_wnd_proc( HWND hwnd, UINT message, WPARAM wp, LPAR /* create the desktop and the associated driver window, and make it the current desktop */ static BOOL create_desktop( HMODULE driver, const WCHAR *name, unsigned int width, unsigned int height ) { - BOOL ret = FALSE; + BOOL ret = TRUE; BOOL (CDECL *create_desktop_func)(unsigned int, unsigned int); if (driver) From a9f7cafbb8e1c20588f1df515cb833d76bc0e0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 14:33:06 +0200 Subject: [PATCH 488/758] explorer: Don't call driver create_desktop if desktop name is "root". (cherry picked from commit 0edc848bee16ae2115f47e2a09435cc1b3834eb0) CW-Bug-Id: #21879 --- dlls/winex11.drv/desktop.c | 11 ----------- programs/explorer/desktop.c | 6 ++++-- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index 351552674e3..5d9a5e7a2cc 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -359,21 +359,10 @@ void X11DRV_init_desktop( Window win, unsigned int width, unsigned int height ) */ NTSTATUS x11drv_create_desktop( void *arg ) { - static const WCHAR rootW[] = {'r','o','o','t',0}; const struct create_desktop_params *params = arg; XSetWindowAttributes win_attr; Window win; Display *display = thread_init_display(); - WCHAR name[MAX_PATH]; - - if (!NtUserGetObjectInformation( NtUserGetThreadDesktop( GetCurrentThreadId() ), - UOI_NAME, name, sizeof(name), NULL )) - name[0] = 0; - - TRACE( "%s %ux%u\n", debugstr_w(name), params->width, params->height ); - - /* magic: desktop "root" means use the root window */ - if (!wcsicmp( name, rootW )) return FALSE; /* Create window */ win_attr.event_mask = ExposureMask | FocusChangeMask | EnterWindowMask | diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 11f81b1d800..c5490c06d97 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -42,7 +42,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(explorer); static const WCHAR default_driver[] = {'m','a','c',',','x','1','1',0}; -static BOOL using_root; +static BOOL using_root = TRUE; struct launcher { @@ -1120,6 +1120,8 @@ void manage_desktop( WCHAR *arg ) if (name && width && height) { + /* magic: desktop "root" means use the root window */ + using_root = !wcsicmp( name, L"root" ); if (!(desktop = CreateDesktopW( name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ))) { WINE_ERR( "failed to create desktop %s error %ld\n", wine_dbgstr_w(name), GetLastError() ); @@ -1140,7 +1142,7 @@ void manage_desktop( WCHAR *arg ) desktop_orig_wndproc = (WNDPROC)SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)desktop_wnd_proc ); - using_root = !desktop || !create_desktop( graphics_driver, name, width, height ); + if (!using_root) using_root = !create_desktop( graphics_driver, name, width, height ); SendMessageW( hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconW( 0, MAKEINTRESOURCEW(OIC_WINLOGO))); if (name) set_desktop_window_title( hwnd, name ); SetWindowPos( hwnd, 0, GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN), From a0bc429c4f0937221f92878bddede1f07151b11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 12:43:38 +0200 Subject: [PATCH 489/758] explorer: Call user driver through a new CreateDesktop callback. (cherry picked from commit f7d45533d13e5d98e1b280b569123d80cfb23f2a) CW-Bug-Id: #21879 --- dlls/user32/winstation.c | 2 +- dlls/win32u/driver.c | 12 ++++++++++++ dlls/win32u/winstation.c | 12 +++++++++++- include/ntuser.h | 3 +++ include/wine/gdi_driver.h | 1 + programs/explorer/desktop.c | 6 ++++-- 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/dlls/user32/winstation.c b/dlls/user32/winstation.c index 23844482f2c..62593ca046f 100644 --- a/dlls/user32/winstation.c +++ b/dlls/user32/winstation.c @@ -256,7 +256,7 @@ HDESK WINAPI CreateDesktopW( LPCWSTR name, LPCWSTR device, LPDEVMODEW devmode, OBJECT_ATTRIBUTES attr; UNICODE_STRING str; - if (device || devmode) + if (device || (devmode && !(flags & DF_WINE_CREATE_DESKTOP))) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 2d5d987652c..7a2e4c3bcdd 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -783,6 +783,11 @@ static BOOL nulldrv_UpdateDisplayDevices( const struct gdi_device_manager *manag return FALSE; } +static BOOL nulldrv_CreateDesktop( const WCHAR *name, UINT width, UINT height ) +{ + return TRUE; +} + static BOOL nodrv_CreateWindow( HWND hwnd ) { static int warned; @@ -1149,6 +1154,11 @@ static BOOL loaderdrv_UpdateDisplayDevices( const struct gdi_device_manager *man return load_driver()->pUpdateDisplayDevices( manager, force, param ); } +static BOOL loaderdrv_CreateDesktop( const WCHAR *name, UINT width, UINT height ) +{ + return load_driver()->pCreateDesktop( name, width, height ); +} + static BOOL loaderdrv_CreateWindow( HWND hwnd ) { return load_driver()->pCreateWindow( hwnd ); @@ -1222,6 +1232,7 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_GetDisplayDepth, loaderdrv_UpdateDisplayDevices, /* windowing functions */ + loaderdrv_CreateDesktop, loaderdrv_CreateWindow, nulldrv_DesktopWindowProc, nulldrv_DestroyWindow, @@ -1300,6 +1311,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(GetCurrentDisplaySettings); SET_USER_FUNC(GetDisplayDepth); SET_USER_FUNC(UpdateDisplayDevices); + SET_USER_FUNC(CreateDesktop); SET_USER_FUNC(CreateWindow); SET_USER_FUNC(DesktopWindowProc); SET_USER_FUNC(DestroyWindow); diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 79dfa4671ac..8ab6122709e 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -141,9 +141,10 @@ HDESK WINAPI NtUserCreateDesktopEx( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *dev DEVMODEW *devmode, DWORD flags, ACCESS_MASK access, ULONG heap_size ) { + WCHAR buffer[MAX_PATH]; HANDLE ret; - if ((device && device->Length) || devmode) + if ((device && device->Length) || (devmode && !(flags & DF_WINE_CREATE_DESKTOP))) { RtlSetLastWin32Error( ERROR_INVALID_PARAMETER ); return 0; @@ -163,6 +164,15 @@ HDESK WINAPI NtUserCreateDesktopEx( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *dev ret = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; + if (!devmode) return ret; + + lstrcpynW( buffer, attr->ObjectName->Buffer, attr->ObjectName->Length / sizeof(WCHAR) + 1 ); + if (!user_driver->pCreateDesktop( buffer, devmode->dmPelsWidth, devmode->dmPelsHeight )) + { + NtUserCloseDesktop( ret ); + return 0; + } + return ret; } diff --git a/include/ntuser.h b/include/ntuser.h index f9c63a48cee..1e2b47e627d 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -286,6 +286,9 @@ struct unpack_dde_message_params #define SPY_RESULT_OK 0x0001 #define SPY_RESULT_DEFWND 0x0002 +/* CreateDesktop wine specific flag */ +#define DF_WINE_CREATE_DESKTOP 0x80000000 + /* NtUserMessageCall codes */ enum { diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index f94dc588fc8..29864a6eb47 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -306,6 +306,7 @@ struct user_driver_funcs INT (*pGetDisplayDepth)(LPCWSTR,BOOL); BOOL (*pUpdateDisplayDevices)(const struct gdi_device_manager *,BOOL,void*); /* windowing functions */ + BOOL (*pCreateDesktop)(const WCHAR *,UINT,UINT); BOOL (*pCreateWindow)(HWND); LRESULT (*pDesktopWindowProc)(HWND,UINT,WPARAM,LPARAM); void (*pDestroyWindow)(HWND); diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index c5490c06d97..9e86aa9c076 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -1120,9 +1120,11 @@ void manage_desktop( WCHAR *arg ) if (name && width && height) { + DEVMODEW devmode = {.dmPelsWidth = width, .dmPelsHeight = height}; /* magic: desktop "root" means use the root window */ - using_root = !wcsicmp( name, L"root" ); - if (!(desktop = CreateDesktopW( name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ))) + if ((using_root = !wcsicmp( name, L"root" ))) desktop = CreateDesktopW( name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); + else desktop = CreateDesktopW( name, NULL, &devmode, DF_WINE_CREATE_DESKTOP, DESKTOP_ALL_ACCESS, NULL ); + if (!desktop) { WINE_ERR( "failed to create desktop %s error %ld\n", wine_dbgstr_w(name), GetLastError() ); ExitProcess( 1 ); From c24a3e7bdc4621d396dfdb1cc84adb8a1ea5515a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 14:12:25 +0200 Subject: [PATCH 490/758] wineandroid: Use the user driver interface to create host desktops. (cherry picked from commit e0d3683d8947cd9a3cb20acdf69091b183a90861) CW-Bug-Id: #21879 --- dlls/wineandroid.drv/android.h | 2 +- dlls/wineandroid.drv/dllmain.c | 9 --------- dlls/wineandroid.drv/init.c | 2 +- dlls/wineandroid.drv/unixlib.h | 1 - dlls/wineandroid.drv/window.c | 4 ++-- dlls/wineandroid.drv/wineandroid.drv.spec | 2 -- 6 files changed, 4 insertions(+), 16 deletions(-) diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h index 0d073a63bcc..d12cfa28f04 100644 --- a/dlls/wineandroid.drv/android.h +++ b/dlls/wineandroid.drv/android.h @@ -87,6 +87,7 @@ extern INT ANDROID_GetKeyNameText( LONG lparam, LPWSTR buffer, INT size ) DECLSP extern UINT ANDROID_MapVirtualKeyEx( UINT code, UINT maptype, HKL hkl ) DECLSPEC_HIDDEN; extern SHORT ANDROID_VkKeyScanEx( WCHAR ch, HKL hkl ) DECLSPEC_HIDDEN; extern void ANDROID_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; +extern BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) DECLSPEC_HIDDEN; extern BOOL ANDROID_CreateWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern void ANDROID_DestroyWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL ANDROID_ProcessEvents( DWORD mask ) DECLSPEC_HIDDEN; @@ -112,7 +113,6 @@ extern void ANDROID_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_fla /* unixlib interface */ -extern NTSTATUS android_create_desktop( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS android_dispatch_ioctl( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS android_java_init( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS android_java_uninit( void *arg ) DECLSPEC_HIDDEN; diff --git a/dlls/wineandroid.drv/dllmain.c b/dlls/wineandroid.drv/dllmain.c index 3f6dbd388eb..320e47e88d2 100644 --- a/dlls/wineandroid.drv/dllmain.c +++ b/dlls/wineandroid.drv/dllmain.c @@ -132,12 +132,3 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) return TRUE; } - - -/*********************************************************************** - * wine_create_desktop (wineandroid.@) - */ -BOOL CDECL wine_create_desktop( UINT width, UINT height ) -{ - return ANDROID_CALL( create_desktop, NULL ); -} diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c index 074aa6c6257..50659171f08 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -349,6 +349,7 @@ static const struct user_driver_funcs android_drv_funcs = .pChangeDisplaySettings = ANDROID_ChangeDisplaySettings, .pGetCurrentDisplaySettings = ANDROID_GetCurrentDisplaySettings, .pUpdateDisplayDevices = ANDROID_UpdateDisplayDevices, + .pCreateDesktop = ANDROID_CreateDesktop, .pCreateWindow = ANDROID_CreateWindow, .pDesktopWindowProc = ANDROID_DesktopWindowProc, .pDestroyWindow = ANDROID_DestroyWindow, @@ -609,7 +610,6 @@ static HRESULT android_init( void *arg ) const unixlib_entry_t __wine_unix_call_funcs[] = { - android_create_desktop, android_dispatch_ioctl, android_init, android_java_init, diff --git a/dlls/wineandroid.drv/unixlib.h b/dlls/wineandroid.drv/unixlib.h index a180e6660c8..f1ba25720fd 100644 --- a/dlls/wineandroid.drv/unixlib.h +++ b/dlls/wineandroid.drv/unixlib.h @@ -21,7 +21,6 @@ enum android_funcs { - unix_create_desktop, unix_dispatch_ioctl, unix_init, unix_java_init, diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index eb96300da89..905f4672580 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -1670,9 +1670,9 @@ LRESULT ANDROID_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) /*********************************************************************** - * android_create_desktop + * ANDROID_CreateDesktop */ -NTSTATUS android_create_desktop( void *arg ) +BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { /* wait until we receive the surface changed event */ while (!screen_width) diff --git a/dlls/wineandroid.drv/wineandroid.drv.spec b/dlls/wineandroid.drv/wineandroid.drv.spec index 22b97356521..e69de29bb2d 100644 --- a/dlls/wineandroid.drv/wineandroid.drv.spec +++ b/dlls/wineandroid.drv/wineandroid.drv.spec @@ -1,2 +0,0 @@ -# Desktop -@ cdecl wine_create_desktop(long long) From ac9ffbf1513fcf8b0bebe91f2e7f135ff954a1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 14:12:25 +0200 Subject: [PATCH 491/758] winex11: Use the user driver interface to create host desktops. (cherry picked from commit 4fcbbf8c9a15c9f18259d5c57b61fa8974121964) CW-Bug-Id: #21879 --- dlls/winex11.drv/desktop.c | 22 +++++++--------------- dlls/winex11.drv/dllmain.c | 10 ---------- dlls/winex11.drv/init.c | 1 + dlls/winex11.drv/unixlib.h | 8 -------- dlls/winex11.drv/window.c | 29 +++++++++++++++++++++++++---- dlls/winex11.drv/winex11.drv.spec | 3 --- dlls/winex11.drv/x11drv.h | 3 +-- dlls/winex11.drv/x11drv_main.c | 2 -- 8 files changed, 34 insertions(+), 44 deletions(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index 5d9a5e7a2cc..66cbee171a5 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -348,22 +348,22 @@ void X11DRV_init_desktop( Window win, unsigned int width, unsigned int height ) desktop_handler.free_monitors = X11DRV_desktop_free_monitors; desktop_handler.register_event_handlers = NULL; TRACE("Display device functions are now handled by: Virtual Desktop\n"); - X11DRV_DisplayDevices_Init( TRUE ); } /*********************************************************************** - * x11drv_create_desktop + * X11DRV_CreateDesktop * * Create the X11 desktop window for the desktop mode. */ -NTSTATUS x11drv_create_desktop( void *arg ) +BOOL X11DRV_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { - const struct create_desktop_params *params = arg; XSetWindowAttributes win_attr; Window win; Display *display = thread_init_display(); + TRACE( "%s %ux%u\n", debugstr_w(name), width, height ); + /* Create window */ win_attr.event_mask = ExposureMask | FocusChangeMask | EnterWindowMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; @@ -377,21 +377,13 @@ NTSTATUS x11drv_create_desktop( void *arg ) win_attr.colormap = None; win = XCreateWindow( display, DefaultRootWindow(display), - 0, 0, params->width, params->height, 0, default_visual.depth, InputOutput, + 0, 0, width, height, 0, default_visual.depth, InputOutput, default_visual.visual, CWEventMask | CWCursor | CWColormap, &win_attr ); if (!win) return FALSE; X11DRV_XInput2_Enable( display, win, win_attr.event_mask ); - if (!create_desktop_win_data( win )) return FALSE; - - X11DRV_init_desktop( win, params->width, params->height ); - if (is_desktop_fullscreen()) - { - TRACE("setting desktop to fullscreen\n"); - XChangeProperty( display, win, x11drv_atom(_NET_WM_STATE), XA_ATOM, 32, - PropModeReplace, (unsigned char*)&x11drv_atom(_NET_WM_STATE_FULLSCREEN), - 1); - } XFlush( display ); + + X11DRV_init_desktop( win, width, height ); return TRUE; } diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index f1553152dcf..bed88671131 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -110,16 +110,6 @@ BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) return TRUE; } - -/*********************************************************************** - * wine_create_desktop (winex11.@) - */ -BOOL CDECL wine_create_desktop( UINT width, UINT height ) -{ - struct create_desktop_params params = { .width = width, .height = height }; - return X11DRV_CALL( create_desktop, ¶ms ); -} - /*********************************************************************** * AttachEventQueueToTablet (winex11.@) */ diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index 9b457b6ccd8..173822b455f 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -411,6 +411,7 @@ static const struct user_driver_funcs x11drv_funcs = .pGetCurrentDisplaySettings = X11DRV_GetCurrentDisplaySettings, .pGetDisplayDepth = X11DRV_GetDisplayDepth, .pUpdateDisplayDevices = X11DRV_UpdateDisplayDevices, + .pCreateDesktop = X11DRV_CreateDesktop, .pCreateWindow = X11DRV_CreateWindow, .pDesktopWindowProc = X11DRV_DesktopWindowProc, .pDestroyWindow = X11DRV_DestroyWindow, diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 45377effdc7..7dc911d6846 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -21,7 +21,6 @@ enum x11drv_funcs { - unix_create_desktop, unix_init, unix_systray_clear, unix_systray_dock, @@ -37,13 +36,6 @@ enum x11drv_funcs #define X11DRV_CALL(func, params) WINE_UNIX_CALL( unix_ ## func, params ) -/* x11drv_create_desktop params */ -struct create_desktop_params -{ - UINT width; - UINT height; -}; - /* x11drv_init params */ struct init_params { diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 9c030f61f14..c8b52e16d97 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2295,13 +2295,13 @@ BOOL X11DRV_DestroyNotify( HWND hwnd, XEvent *event ) /* initialize the desktop window id in the desktop manager process */ -BOOL create_desktop_win_data( Window win ) +static BOOL create_desktop_win_data( Window win, HWND hwnd ) { struct x11drv_thread_data *thread_data = x11drv_thread_data(); Display *display = thread_data->display; struct x11drv_win_data *data; - if (!(data = alloc_win_data( display, NtUserGetDesktopWindow() ))) return FALSE; + if (!(data = alloc_win_data( display, hwnd ))) return FALSE; data->whole_window = win; data->managed = TRUE; NtUserSetProp( data->hwnd, whole_window_prop, (HANDLE)win ); @@ -2333,7 +2333,10 @@ void X11DRV_SetDesktopWindow( HWND hwnd ) if (!width && !height) /* not initialized yet */ { - RECT rect = NtUserGetVirtualScreenRect(); + RECT rect; + + X11DRV_DisplayDevices_Init( TRUE ); + rect = NtUserGetVirtualScreenRect(); SERVER_START_REQ( set_window_pos ) { @@ -2348,11 +2351,29 @@ void X11DRV_SetDesktopWindow( HWND hwnd ) wine_server_call( req ); } SERVER_END_REQ; + + if (!is_virtual_desktop()) return; + if (!create_desktop_win_data( root_window, hwnd )) + { + ERR( "Failed to create virtual desktop window data\n" ); + root_window = DefaultRootWindow( gdi_display ); + } + else if (is_desktop_fullscreen()) + { + Display *display = x11drv_thread_data()->display; + TRACE("setting desktop to fullscreen\n"); + XChangeProperty( display, root_window, x11drv_atom(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace, + (unsigned char*)&x11drv_atom(_NET_WM_STATE_FULLSCREEN), 1 ); + } } else { Window win = (Window)NtUserGetProp( hwnd, whole_window_prop ); - if (win && win != root_window) X11DRV_init_desktop( win, width, height ); + if (win && win != root_window) + { + X11DRV_init_desktop( win, width, height ); + X11DRV_DisplayDevices_Init( TRUE ); + } } } diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index a7334eef3d9..6dedae550e8 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -4,8 +4,5 @@ @ cdecl LoadTabletInfo(long) X11DRV_LoadTabletInfo @ cdecl WTInfoW(long long ptr) X11DRV_WTInfoW -# Desktop -@ cdecl wine_create_desktop(long long) - # System tray @ cdecl wine_notify_icon(long ptr) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 089fd849cb9..a2e1998be8d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -224,6 +224,7 @@ extern BOOL X11DRV_GetCurrentDisplaySettings( LPCWSTR name, BOOL is_primary, LPD extern INT X11DRV_GetDisplayDepth( LPCWSTR name, BOOL is_primary ) DECLSPEC_HIDDEN; extern BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manager, BOOL force, void *param ) DECLSPEC_HIDDEN; +extern BOOL X11DRV_CreateDesktop( const WCHAR *name, UINT width, UINT height ) DECLSPEC_HIDDEN; extern BOOL X11DRV_CreateWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern LRESULT X11DRV_DesktopWindowProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) DECLSPEC_HIDDEN; extern void X11DRV_DestroyWindow( HWND hwnd ) DECLSPEC_HIDDEN; @@ -821,7 +822,6 @@ extern void init_registry_display_settings(void) DECLSPEC_HIDDEN; extern BOOL is_virtual_desktop(void) DECLSPEC_HIDDEN; extern BOOL is_desktop_fullscreen(void) DECLSPEC_HIDDEN; extern BOOL is_detached_mode(const DEVMODEW *) DECLSPEC_HIDDEN; -extern BOOL create_desktop_win_data( Window win ) DECLSPEC_HIDDEN; void X11DRV_Settings_Init(void) DECLSPEC_HIDDEN; void X11DRV_XF86VM_Init(void) DECLSPEC_HIDDEN; @@ -900,7 +900,6 @@ static inline BOOL is_window_rect_mapped( const RECT *rect ) /* unixlib interface */ -extern NTSTATUS x11drv_create_desktop( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_systray_clear( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_systray_dock( void *arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_systray_hide( void *arg ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 923b0037217..5e700df456a 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -1485,7 +1485,6 @@ NTSTATUS x11drv_client_call( enum client_callback func, UINT arg ) const unixlib_entry_t __wine_unix_call_funcs[] = { - x11drv_create_desktop, x11drv_init, x11drv_systray_clear, x11drv_systray_dock, @@ -1574,7 +1573,6 @@ static NTSTATUS x11drv_wow64_tablet_info( void *arg ) const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { - x11drv_create_desktop, x11drv_wow64_init, x11drv_wow64_systray_clear, x11drv_wow64_systray_dock, From 7876193594c9b929a23f57e6567db7e3c664c6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 14:12:25 +0200 Subject: [PATCH 492/758] explorer: Remove now unnecessary wine_create_desktop entry. (cherry picked from commit 4b5311c7e02aa7ab2409f8fdcd22233f116dc4bc) CW-Bug-Id: #21879 --- programs/explorer/desktop.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index 9e86aa9c076..cb17eaeb18e 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -770,20 +770,6 @@ static LRESULT WINAPI desktop_wnd_proc( HWND hwnd, UINT message, WPARAM wp, LPAR return desktop_orig_wndproc( hwnd, message, wp, lp ); } -/* create the desktop and the associated driver window, and make it the current desktop */ -static BOOL create_desktop( HMODULE driver, const WCHAR *name, unsigned int width, unsigned int height ) -{ - BOOL ret = TRUE; - BOOL (CDECL *create_desktop_func)(unsigned int, unsigned int); - - if (driver) - { - create_desktop_func = (void *)GetProcAddress( driver, "wine_create_desktop" ); - if (create_desktop_func) ret = create_desktop_func( width, height ); - } - return ret; -} - /* parse the desktop size specification */ static BOOL parse_size( const WCHAR *size, unsigned int *width, unsigned int *height ) { @@ -1144,7 +1130,6 @@ void manage_desktop( WCHAR *arg ) desktop_orig_wndproc = (WNDPROC)SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)desktop_wnd_proc ); - if (!using_root) using_root = !create_desktop( graphics_driver, name, width, height ); SendMessageW( hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconW( 0, MAKEINTRESOURCEW(OIC_WINLOGO))); if (name) set_desktop_window_title( hwnd, name ); SetWindowPos( hwnd, 0, GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN), From 656f294577c6683153886bf2ac5672746bc6f4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 May 2023 11:01:17 +0200 Subject: [PATCH 493/758] imm32: Query the new input context in ImmAssociateContextEx / IACE_DEFAULT. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54991 (cherry picked from commit 7ed63c30e8dee3509c52e11230470be2dcfe6cf5) CW-Bug-Id: #21879 --- dlls/imm32/imm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index aa1881e0918..a17593d18a8 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -1038,6 +1038,7 @@ BOOL WINAPI ImmAssociateContextEx( HWND hwnd, HIMC new_himc, DWORD flags ) ret = NtUserAssociateInputContext( hwnd, new_himc, flags ); if (ret == AICR_FOCUS_CHANGED) { + if (flags == IACE_DEFAULT) new_himc = NtUserGetWindowInputContext( hwnd ); ImmSetActiveContext( hwnd, old_himc, FALSE ); ImmSetActiveContext( hwnd, new_himc, TRUE ); if (hwnd == GetFocus()) set_ime_ui_window_himc( new_himc ); From b7593dbfc65de1c0579676c58e4efd379f1067de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 13:17:34 +0200 Subject: [PATCH 494/758] server: Use get_hardware_msg_bit consistently to classify messages. (cherry picked from commit 8f9610fdff41b26c5129ee7a46c05ecbbf73042f) CW-Bug-Id: #21879 --- server/queue.c | 69 +++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/server/queue.c b/server/queue.c index b1c2bc65218..ba292a430ac 100644 --- a/server/queue.c +++ b/server/queue.c @@ -643,12 +643,6 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits SHARED_WRITE_END( &queue->shared->seq ); } -/* check whether msg is a keyboard message */ -static inline int is_keyboard_msg( struct message *msg ) -{ - return (msg->msg >= WM_KEYFIRST && msg->msg <= WM_KEYLAST); -} - /* check if message is matched by the filter */ static inline int check_msg_filter( unsigned int msg, unsigned int first, unsigned int last ) { @@ -674,10 +668,10 @@ static inline int filter_contains_hw_range( unsigned int first, unsigned int las static inline int get_hardware_msg_bit( struct message *msg ) { if (msg->msg == WM_INPUT_DEVICE_CHANGE || msg->msg == WM_INPUT) return QS_RAWINPUT; - if (msg->msg == WM_MOUSEMOVE || msg->msg == WM_NCMOUSEMOVE || - msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || - msg->msg == WM_POINTERUPDATE) return QS_MOUSEMOVE; - if (is_keyboard_msg( msg )) return QS_KEY; + if (msg->msg == WM_MOUSEMOVE || msg->msg == WM_NCMOUSEMOVE) return QS_MOUSEMOVE; + if (msg->msg >= WM_KEYFIRST && msg->msg <= WM_KEYLAST) return QS_KEY; + if (msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || + msg->msg == WM_POINTERUPDATE) return QS_POINTER; return QS_MOUSEBUTTON; } @@ -1741,26 +1735,28 @@ static user_handle_t find_hardware_message_window( struct desktop *desktop, stru *thread = NULL; *msg_code = msg->msg; - if (msg->msg == WM_INPUT || msg->msg == WM_INPUT_DEVICE_CHANGE || - msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || - msg->msg == WM_POINTERUPDATE) + switch (get_hardware_msg_bit( msg )) { + case QS_POINTER: + case QS_RAWINPUT: if (!(win = msg->win) && input) win = input->shared->focus; - } - else if (is_keyboard_msg( msg )) - { + break; + case QS_KEY: if (input && !(win = input->shared->focus)) { win = input->shared->active; if (*msg_code < WM_SYSKEYDOWN) *msg_code += WM_SYSKEYDOWN - WM_KEYDOWN; } - } - else if (!input || !(win = input->shared->capture)) /* mouse message */ - { - if (is_window_visible( msg->win ) && !is_window_transparent( msg->win )) win = msg->win; - else win = shallow_window_from_point( desktop, msg->x, msg->y ); - - *thread = window_thread_from_point( win, msg->x, msg->y ); + break; + case QS_MOUSEMOVE: + case QS_MOUSEBUTTON: + if (!input || !(win = input->shared->capture)) + { + if (is_window_visible( msg->win ) && !is_window_transparent( msg->win )) win = msg->win; + else win = shallow_window_from_point( desktop, msg->x, msg->y ); + *thread = window_thread_from_point( win, msg->x, msg->y ); + } + break; } if (!*thread) @@ -1804,28 +1800,26 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg last_input_time = get_tick_count(); if (msg->msg != WM_MOUSEMOVE) always_queue = 1; - if (is_keyboard_msg( msg )) + switch (get_hardware_msg_bit( msg )) { + case QS_KEY: if (queue_hotkey_message( desktop, msg )) return; if (desktop->shared->keystate[VK_MENU] & 0x80) msg->lparam |= KF_ALTDOWN << 16; if (msg->wparam == VK_SHIFT || msg->wparam == VK_LSHIFT || msg->wparam == VK_RSHIFT) msg->lparam &= ~(KF_EXTENDED << 16); - } - else if (msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || msg->msg == WM_POINTERUPDATE) - { + break; + case QS_POINTER: if (IS_POINTER_PRIMARY_WPARAM( msg_data->rawinput.mouse.data )) { prepend_cursor_history( msg->x, msg->y, msg->time, msg_data->info ); if (update_desktop_cursor_pos( desktop, msg->x, msg->y )) always_queue = 1; } - } - else if (msg->msg != WM_INPUT && msg->msg != WM_INPUT_DEVICE_CHANGE) - { - if (msg->msg == WM_MOUSEMOVE) - { - prepend_cursor_history( msg->x, msg->y, msg->time, msg_data->info ); - if (update_desktop_cursor_pos( desktop, msg->x, msg->y )) always_queue = 1; - } + break; + case QS_MOUSEMOVE: + prepend_cursor_history( msg->x, msg->y, msg->time, msg_data->info ); + if (update_desktop_cursor_pos( desktop, msg->x, msg->y )) always_queue = 1; + /* fallthrough */ + case QS_MOUSEBUTTON: if (desktop->shared->keystate[VK_LBUTTON] & 0x80) msg->wparam |= MK_LBUTTON; if (desktop->shared->keystate[VK_MBUTTON] & 0x80) msg->wparam |= MK_MBUTTON; if (desktop->shared->keystate[VK_RBUTTON] & 0x80) msg->wparam |= MK_RBUTTON; @@ -1833,6 +1827,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg if (desktop->shared->keystate[VK_CONTROL] & 0x80) msg->wparam |= MK_CONTROL; if (desktop->shared->keystate[VK_XBUTTON1] & 0x80) msg->wparam |= MK_XBUTTON1; if (desktop->shared->keystate[VK_XBUTTON2] & 0x80) msg->wparam |= MK_XBUTTON2; + break; } msg->x = desktop->shared->cursor.x; msg->y = desktop->shared->cursor.y; @@ -2518,9 +2513,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user data->hw_id = msg->unique_id; set_reply_data( msg->data, msg->data_size ); - if ((msg->msg == WM_INPUT || msg->msg == WM_INPUT_DEVICE_CHANGE || - msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || - msg->msg == WM_POINTERUPDATE) && (flags & PM_REMOVE)) + if ((get_hardware_msg_bit( msg ) & (QS_POINTER | QS_RAWINPUT)) && (flags & PM_REMOVE)) release_hardware_message( current->queue, data->hw_id ); return 1; } From 1606ce7fcee4b3d47f906dba80151a8949e0083b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 13:27:01 +0200 Subject: [PATCH 495/758] fixup! server: Move the desktop keystate to shared memory. --- server/user.h | 1 - 1 file changed, 1 deletion(-) diff --git a/server/user.h b/server/user.h index c8dc6e922d8..1de900ae310 100644 --- a/server/user.h +++ b/server/user.h @@ -67,7 +67,6 @@ struct desktop struct list touches; /* list of active touches */ struct thread_input *foreground_input; /* thread input of foreground thread */ unsigned int users; /* processes and threads using this desktop */ - unsigned char keystate[256]; /* asynchronous key state */ user_handle_t cursor_win; /* window that contains the cursor */ struct object *shared_mapping; /* desktop shared memory mapping */ volatile struct desktop_shared_memory *shared; /* desktop shared memory ptr */ From d6817cf4ffbd999dfa84210f274d8f1839ce0740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 22:55:34 +0200 Subject: [PATCH 496/758] win32u: Move some window functions to window.c. (cherry picked from commit c22c10d6e923ea3fb53412b3f218bf9eeeef4cb2) CW-Bug-Id: #21879 --- dlls/win32u/input.c | 112 ---------------------------------- dlls/win32u/win32u_private.h | 10 ++-- dlls/win32u/window.c | 113 +++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 117 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 24321d6d5a2..98642c3605c 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2513,118 +2513,6 @@ BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_P return FALSE; } -HWND get_shell_window(void) -{ - HWND hwnd = 0; - - SERVER_START_REQ(set_global_windows) - { - req->flags = 0; - if (!wine_server_call_err(req)) - hwnd = wine_server_ptr_handle( reply->old_shell_window ); - } - SERVER_END_REQ; - - return hwnd; -} - -/*********************************************************************** -* NtUserSetShellWindowEx (win32u.@) -*/ -BOOL WINAPI NtUserSetShellWindowEx( HWND shell, HWND list_view ) -{ - BOOL ret; - - /* shell = Progman[Program Manager] - * |-> SHELLDLL_DefView - * list_view = | |-> SysListView32 - * | | |-> tooltips_class32 - * | | - * | |-> SysHeader32 - * | - * |-> ProxyTarget - */ - - if (get_shell_window()) - return FALSE; - - if (get_window_long( shell, GWL_EXSTYLE ) & WS_EX_TOPMOST) - return FALSE; - - if (list_view != shell && (get_window_long( list_view, GWL_EXSTYLE ) & WS_EX_TOPMOST)) - return FALSE; - - if (list_view && list_view != shell) - NtUserSetWindowPos( list_view, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ); - - NtUserSetWindowPos( shell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ); - - SERVER_START_REQ(set_global_windows) - { - req->flags = SET_GLOBAL_SHELL_WINDOWS; - req->shell_window = wine_server_user_handle( shell ); - req->shell_listview = wine_server_user_handle( list_view ); - ret = !wine_server_call_err(req); - } - SERVER_END_REQ; - return ret; -} - -HWND get_progman_window(void) -{ - HWND ret = 0; - - SERVER_START_REQ(set_global_windows) - { - req->flags = 0; - if (!wine_server_call_err(req)) - ret = wine_server_ptr_handle( reply->old_progman_window ); - } - SERVER_END_REQ; - return ret; -} - -HWND set_progman_window( HWND hwnd ) -{ - SERVER_START_REQ(set_global_windows) - { - req->flags = SET_GLOBAL_PROGMAN_WINDOW; - req->progman_window = wine_server_user_handle( hwnd ); - if (wine_server_call_err( req )) hwnd = 0; - } - SERVER_END_REQ; - return hwnd; -} - -HWND get_taskman_window(void) -{ - HWND ret = 0; - - SERVER_START_REQ(set_global_windows) - { - req->flags = 0; - if (!wine_server_call_err(req)) - ret = wine_server_ptr_handle( reply->old_taskman_window ); - } - SERVER_END_REQ; - return ret; -} - -HWND set_taskman_window( HWND hwnd ) -{ - /* hwnd = MSTaskSwWClass - * |-> SysTabControl32 - */ - SERVER_START_REQ(set_global_windows) - { - req->flags = SET_GLOBAL_TASKMAN_WINDOW; - req->taskman_window = wine_server_user_handle( hwnd ); - if (wine_server_call_err( req )) hwnd = 0; - } - SERVER_END_REQ; - return hwnd; -} - /***************************************************************************** * NtUserGetTouchInputInfo (WIN32U.@) */ diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index a885128d3a7..29cc0c46476 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -271,17 +271,12 @@ extern HWND get_capture(void) DECLSPEC_HIDDEN; extern BOOL get_cursor_pos( POINT *pt ) DECLSPEC_HIDDEN; extern HWND get_focus(void) DECLSPEC_HIDDEN; extern DWORD get_input_state(void) DECLSPEC_HIDDEN; -extern HWND get_progman_window(void) DECLSPEC_HIDDEN; -extern HWND get_shell_window(void) DECLSPEC_HIDDEN; -extern HWND get_taskman_window(void) DECLSPEC_HIDDEN; extern BOOL register_touch_window( HWND hwnd, UINT flags ) DECLSPEC_HIDDEN; extern BOOL WINAPI release_capture(void) DECLSPEC_HIDDEN; extern BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) DECLSPEC_HIDDEN; extern BOOL set_caret_blink_time( unsigned int time ) DECLSPEC_HIDDEN; extern BOOL set_caret_pos( int x, int y ) DECLSPEC_HIDDEN; extern BOOL set_foreground_window( HWND hwnd, BOOL mouse ) DECLSPEC_HIDDEN; -extern HWND set_progman_window( HWND hwnd ) DECLSPEC_HIDDEN; -extern HWND set_taskman_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void toggle_caret( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL unregister_touch_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN; @@ -411,6 +406,11 @@ extern ULONG set_window_style( HWND hwnd, ULONG set_bits, ULONG clear_bits ) DEC extern BOOL show_owned_popups( HWND owner, BOOL show ) DECLSPEC_HIDDEN; extern void update_window_state( HWND hwnd ) DECLSPEC_HIDDEN; extern HWND window_from_point( HWND hwnd, POINT pt, INT *hittest ) DECLSPEC_HIDDEN; +extern HWND get_shell_window(void) DECLSPEC_HIDDEN; +extern HWND get_progman_window(void) DECLSPEC_HIDDEN; +extern HWND set_progman_window( HWND hwnd ) DECLSPEC_HIDDEN; +extern HWND get_taskman_window(void) DECLSPEC_HIDDEN; +extern HWND set_taskman_window( HWND hwnd ) DECLSPEC_HIDDEN; /* to release pointers retrieved by win_get_ptr */ static inline void release_win_ptr( struct tagWND *ptr ) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 95569ba187a..81a13c7ad65 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -5682,3 +5682,116 @@ DWORD WINAPI NtUserDragObject( HWND parent, HWND hwnd, UINT fmt, ULONG_PTR data, return 0; } + + +HWND get_shell_window(void) +{ + HWND hwnd = 0; + + SERVER_START_REQ(set_global_windows) + { + req->flags = 0; + if (!wine_server_call_err(req)) + hwnd = wine_server_ptr_handle( reply->old_shell_window ); + } + SERVER_END_REQ; + + return hwnd; +} + +/*********************************************************************** +* NtUserSetShellWindowEx (win32u.@) +*/ +BOOL WINAPI NtUserSetShellWindowEx( HWND shell, HWND list_view ) +{ + BOOL ret; + + /* shell = Progman[Program Manager] + * |-> SHELLDLL_DefView + * list_view = | |-> SysListView32 + * | | |-> tooltips_class32 + * | | + * | |-> SysHeader32 + * | + * |-> ProxyTarget + */ + + if (get_shell_window()) + return FALSE; + + if (get_window_long( shell, GWL_EXSTYLE ) & WS_EX_TOPMOST) + return FALSE; + + if (list_view != shell && (get_window_long( list_view, GWL_EXSTYLE ) & WS_EX_TOPMOST)) + return FALSE; + + if (list_view && list_view != shell) + NtUserSetWindowPos( list_view, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ); + + NtUserSetWindowPos( shell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ); + + SERVER_START_REQ(set_global_windows) + { + req->flags = SET_GLOBAL_SHELL_WINDOWS; + req->shell_window = wine_server_user_handle( shell ); + req->shell_listview = wine_server_user_handle( list_view ); + ret = !wine_server_call_err(req); + } + SERVER_END_REQ; + return ret; +} + +HWND get_progman_window(void) +{ + HWND ret = 0; + + SERVER_START_REQ(set_global_windows) + { + req->flags = 0; + if (!wine_server_call_err(req)) + ret = wine_server_ptr_handle( reply->old_progman_window ); + } + SERVER_END_REQ; + return ret; +} + +HWND set_progman_window( HWND hwnd ) +{ + SERVER_START_REQ(set_global_windows) + { + req->flags = SET_GLOBAL_PROGMAN_WINDOW; + req->progman_window = wine_server_user_handle( hwnd ); + if (wine_server_call_err( req )) hwnd = 0; + } + SERVER_END_REQ; + return hwnd; +} + +HWND get_taskman_window(void) +{ + HWND ret = 0; + + SERVER_START_REQ(set_global_windows) + { + req->flags = 0; + if (!wine_server_call_err(req)) + ret = wine_server_ptr_handle( reply->old_taskman_window ); + } + SERVER_END_REQ; + return ret; +} + +HWND set_taskman_window( HWND hwnd ) +{ + /* hwnd = MSTaskSwWClass + * |-> SysTabControl32 + */ + SERVER_START_REQ(set_global_windows) + { + req->flags = SET_GLOBAL_TASKMAN_WINDOW; + req->taskman_window = wine_server_user_handle( hwnd ); + if (wine_server_call_err( req )) hwnd = 0; + } + SERVER_END_REQ; + return hwnd; +} From 38f9ca5c19e8424800c1a00cd5db0923d669410a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 22:04:20 +0200 Subject: [PATCH 497/758] win32u: Move cursor clipping functions to input.c. (cherry picked from commit 1cea2be9e822d25b508f8b709d58602768b37d2d) CW-Bug-Id: #21879 --- dlls/win32u/cursoricon.c | 71 ------------------------------------ dlls/win32u/input.c | 71 ++++++++++++++++++++++++++++++++++++ dlls/win32u/win32u_private.h | 2 +- 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/dlls/win32u/cursoricon.c b/dlls/win32u/cursoricon.c index ec143b841fd..bfcb805901c 100644 --- a/dlls/win32u/cursoricon.c +++ b/dlls/win32u/cursoricon.c @@ -150,77 +150,6 @@ HCURSOR WINAPI NtUserGetCursor(void) return ret; } -/*********************************************************************** - * NtUserClipCursor (win32u.@) - */ -BOOL WINAPI NtUserClipCursor( const RECT *rect ) -{ - UINT dpi; - BOOL ret; - RECT new_rect; - - TRACE( "Clipping to %s\n", wine_dbgstr_rect(rect) ); - - if (rect) - { - if (rect->left > rect->right || rect->top > rect->bottom) return FALSE; - if ((dpi = get_thread_dpi())) - { - HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, dpi ); - new_rect = map_dpi_rect( *rect, dpi, get_monitor_dpi( monitor )); - rect = &new_rect; - } - } - - SERVER_START_REQ( set_cursor ) - { - if (rect) - { - req->flags = SET_CURSOR_CLIP; - req->clip.left = rect->left; - req->clip.top = rect->top; - req->clip.right = rect->right; - req->clip.bottom = rect->bottom; - } - else req->flags = SET_CURSOR_NOCLIP; - - if ((ret = !wine_server_call( req ))) - { - new_rect.left = reply->new_clip.left; - new_rect.top = reply->new_clip.top; - new_rect.right = reply->new_clip.right; - new_rect.bottom = reply->new_clip.bottom; - } - } - SERVER_END_REQ; - if (ret) user_driver->pClipCursor( &new_rect ); - return ret; -} - -BOOL get_clip_cursor( RECT *rect ) -{ - volatile struct desktop_shared_memory *shared = get_desktop_shared_memory(); - UINT dpi; - - if (!rect || !shared) return FALSE; - - SHARED_READ_BEGIN( &shared->seq ) - { - rect->left = shared->cursor.clip.left; - rect->top = shared->cursor.clip.top; - rect->right = shared->cursor.clip.right; - rect->bottom = shared->cursor.clip.bottom; - } - SHARED_READ_END( &shared->seq ); - - if ((dpi = get_thread_dpi())) - { - HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, 0 ); - *rect = map_dpi_rect( *rect, get_monitor_dpi( monitor ), dpi ); - } - return TRUE; -} - HICON alloc_cursoricon_handle( BOOL is_icon ) { struct cursoricon_object *obj; diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 98642c3605c..244df7a7cec 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2513,6 +2513,77 @@ BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_P return FALSE; } +BOOL get_clip_cursor( RECT *rect ) +{ + volatile struct desktop_shared_memory *shared = get_desktop_shared_memory(); + UINT dpi; + + if (!rect || !shared) return FALSE; + + SHARED_READ_BEGIN( &shared->seq ) + { + rect->left = shared->cursor.clip.left; + rect->top = shared->cursor.clip.top; + rect->right = shared->cursor.clip.right; + rect->bottom = shared->cursor.clip.bottom; + } + SHARED_READ_END( &shared->seq ); + + if ((dpi = get_thread_dpi())) + { + HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, 0 ); + *rect = map_dpi_rect( *rect, get_monitor_dpi( monitor ), dpi ); + } + return TRUE; +} + +/*********************************************************************** + * NtUserClipCursor (win32u.@) + */ +BOOL WINAPI NtUserClipCursor( const RECT *rect ) +{ + UINT dpi; + BOOL ret; + RECT new_rect; + + TRACE( "Clipping to %s\n", wine_dbgstr_rect(rect) ); + + if (rect) + { + if (rect->left > rect->right || rect->top > rect->bottom) return FALSE; + if ((dpi = get_thread_dpi())) + { + HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, dpi ); + new_rect = map_dpi_rect( *rect, dpi, get_monitor_dpi( monitor )); + rect = &new_rect; + } + } + + SERVER_START_REQ( set_cursor ) + { + if (rect) + { + req->flags = SET_CURSOR_CLIP; + req->clip.left = rect->left; + req->clip.top = rect->top; + req->clip.right = rect->right; + req->clip.bottom = rect->bottom; + } + else req->flags = SET_CURSOR_NOCLIP; + + if ((ret = !wine_server_call( req ))) + { + new_rect.left = reply->new_clip.left; + new_rect.top = reply->new_clip.top; + new_rect.right = reply->new_clip.right; + new_rect.bottom = reply->new_clip.bottom; + } + } + SERVER_END_REQ; + if (ret) user_driver->pClipCursor( &new_rect ); + return ret; +} + /***************************************************************************** * NtUserGetTouchInputInfo (WIN32U.@) */ diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 29cc0c46476..dc653258d66 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -216,7 +216,6 @@ extern void release_clipboard_owner( HWND hwnd ) DECLSPEC_HIDDEN; /* cursoricon.c */ extern HICON alloc_cursoricon_handle( BOOL is_icon ) DECLSPEC_HIDDEN; -extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN; extern ULONG_PTR get_icon_param( HICON handle ) DECLSPEC_HIDDEN; extern ULONG_PTR set_icon_param( HICON handle, ULONG_PTR param ) DECLSPEC_HIDDEN; @@ -280,6 +279,7 @@ extern BOOL set_foreground_window( HWND hwnd, BOOL mouse ) DECLSPEC_HIDDEN; extern void toggle_caret( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL unregister_touch_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN; +extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN; /* menu.c */ extern HMENU create_menu( BOOL is_popup ) DECLSPEC_HIDDEN; From 2001e78eb52fbf182e11c08e1ba05b25769da30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 22:18:32 +0200 Subject: [PATCH 498/758] win32u: Add a separate function to process WM_WINE_CLIPCURSOR. (cherry picked from commit 3bce247bd2c6b678d23c271fb5a8c8867a1be3f2) CW-Bug-Id: #21879 --- dlls/win32u/input.c | 12 ++++++++++++ dlls/win32u/message.c | 8 +------- dlls/win32u/win32u_private.h | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 244df7a7cec..e6a49ce7a1d 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2537,6 +2537,18 @@ BOOL get_clip_cursor( RECT *rect ) return TRUE; } +BOOL process_wine_clipcursor( BOOL empty ) +{ + RECT rect; + + TRACE( "empty %u\n", empty ); + + if (empty) return user_driver->pClipCursor( NULL ); + + get_clip_cursor( &rect ); + return user_driver->pClipCursor( &rect ); +} + /*********************************************************************** * NtUserClipCursor (win32u.@) */ diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 8c7548a396a..eb7a3aa37b1 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1287,13 +1287,7 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam ); } case WM_WINE_CLIPCURSOR: - if (wparam) - { - RECT rect; - get_clip_cursor( &rect ); - return user_driver->pClipCursor( &rect ); - } - return user_driver->pClipCursor( NULL ); + return process_wine_clipcursor( !wparam ); case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index dc653258d66..286df9419a6 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -280,6 +280,7 @@ extern void toggle_caret( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL unregister_touch_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN; +extern BOOL process_wine_clipcursor( BOOL empty ) DECLSPEC_HIDDEN; /* menu.c */ extern HMENU create_menu( BOOL is_popup ) DECLSPEC_HIDDEN; From cdf9d797cbf2b4072bb0caf1c0cba0ed401f445b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 11:23:40 +0200 Subject: [PATCH 499/758] win32u: Use WM_WINE_CLIPCURSOR / TRUE for empty clipping rect. (cherry picked from commit 900ba826540f4b10ad834109e520b097e2dbf415) CW-Bug-Id: #21879 --- dlls/win32u/message.c | 2 +- server/queue.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index eb7a3aa37b1..dead1ab3951 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1287,7 +1287,7 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam ); } case WM_WINE_CLIPCURSOR: - return process_wine_clipcursor( !wparam ); + return process_wine_clipcursor( wparam ); case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; diff --git a/server/queue.c b/server/queue.c index ba292a430ac..79a57bad9db 100644 --- a/server/queue.c +++ b/server/queue.c @@ -559,7 +559,7 @@ static void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect SHARED_WRITE_BEGIN( &desktop->shared->seq ); desktop->shared->cursor.clip = new_rect; - if (send_clip_msg) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, rect != NULL, 0 ); + if (send_clip_msg) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, rect == NULL, 0 ); /* warp the mouse to be inside the clip rect */ x = max( min( desktop->shared->cursor.x, desktop->shared->cursor.clip.right - 1 ), desktop->shared->cursor.clip.left ); From 61e3780821400ecdfada836daf6b1abc509f0b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 11:56:30 +0200 Subject: [PATCH 500/758] user32: Remove now unused virtual screen helpers. (cherry picked from commit 46d276188e306104eb60c113f40372d8cc9c7976) CW-Bug-Id: #21879 --- dlls/user32/sysparams.c | 37 ------------------------------------- dlls/user32/user_private.h | 3 --- 2 files changed, 40 deletions(-) diff --git a/dlls/user32/sysparams.c b/dlls/user32/sysparams.c index c7bd702726a..5a9087881ce 100644 --- a/dlls/user32/sysparams.c +++ b/dlls/user32/sysparams.c @@ -160,43 +160,6 @@ static void SYSPARAMS_NonClientMetrics32ATo32W( const NONCLIENTMETRICSA* lpnm32A /* Helper functions to retrieve monitors info */ -static BOOL CALLBACK get_virtual_screen_proc( HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM lp ) -{ - RECT *virtual_rect = (RECT *)lp; - - UnionRect( virtual_rect, virtual_rect, rect ); - return TRUE; -} - -RECT get_virtual_screen_rect(void) -{ - RECT rect = {0}; - - NtUserEnumDisplayMonitors( 0, NULL, get_virtual_screen_proc, (LPARAM)&rect ); - return rect; -} - -static BOOL CALLBACK get_primary_monitor_proc( HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM lp ) -{ - RECT *primary_rect = (RECT *)lp; - - if (!rect->top && !rect->left && rect->right && rect->bottom) - { - *primary_rect = *rect; - return FALSE; - } - - return TRUE; -} - -RECT get_primary_monitor_rect(void) -{ - RECT rect = {0}; - - NtUserEnumDisplayMonitors( 0, NULL, get_primary_monitor_proc, (LPARAM)&rect ); - return rect; -} - HDC get_display_dc(void) { EnterCriticalSection( &display_dc_section ); diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 430ac826f7e..c3f877758c1 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -55,10 +55,7 @@ extern HANDLE render_synthesized_format( UINT format, UINT from ) DECLSPEC_HIDDE extern void CLIPBOARD_ReleaseOwner( HWND hwnd ) DECLSPEC_HIDDEN; extern HDC get_display_dc(void) DECLSPEC_HIDDEN; extern void release_display_dc( HDC hdc ) DECLSPEC_HIDDEN; -extern void wait_graphics_driver_ready(void) DECLSPEC_HIDDEN; extern void *get_hook_proc( void *proc, const WCHAR *module, HMODULE *free_module ) DECLSPEC_HIDDEN; -extern RECT get_virtual_screen_rect(void) DECLSPEC_HIDDEN; -extern RECT get_primary_monitor_rect(void) DECLSPEC_HIDDEN; extern DWORD get_input_codepage( void ) DECLSPEC_HIDDEN; extern BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping mapping ) DECLSPEC_HIDDEN; extern HPEN SYSCOLOR_GetPen( INT index ) DECLSPEC_HIDDEN; From 4ebd809f7ea36d6ccc00be382b5c54346d6299bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 May 2023 23:27:53 +0200 Subject: [PATCH 501/758] user32/tests: Zero-initialize keyboard state array. (cherry picked from commit 29ac54e3004e2a37b77983253a64e456d5507ffd) CW-Bug-Id: #21879 --- dlls/user32/tests/input.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 5cb02443e44..ae037313576 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -4350,7 +4350,7 @@ static DWORD WINAPI get_key_state_thread(void *arg) struct get_key_state_test_desc* test; HANDLE *semaphores = params->semaphores; DWORD result; - BYTE keystate[256]; + BYTE keystate[256] = {0}; BOOL has_queue; BOOL expect_x, expect_c; MSG msg; @@ -4412,7 +4412,7 @@ static void test_GetKeyState(void) struct get_key_state_thread_params params; HANDLE thread; DWORD result; - BYTE keystate[256]; + BYTE keystate[256] = {0}; BOOL expect_x, expect_c; HWND hwnd; MSG msg; @@ -4425,7 +4425,6 @@ static void test_GetKeyState(void) return; } - memset(keystate, 0, sizeof(keystate)); params.semaphores[0] = CreateSemaphoreA(NULL, 0, 1, NULL); ok(params.semaphores[0] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); params.semaphores[1] = CreateSemaphoreA(NULL, 0, 1, NULL); From 3bdd51c418ec69693ec864973ed8b3d79d7ef96e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 11:45:15 +0200 Subject: [PATCH 502/758] user32/tests: Add a helper to run a test in a process. (cherry picked from commit 1bf316c0d9d9afe2c1af10a3a3b21735a6d88994) CW-Bug-Id: #21879 --- dlls/user32/tests/input.c | 86 +++++++++++++++------------------------ 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index ae037313576..72e8070da52 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -180,6 +180,24 @@ static void init_function_pointers(void) is_wow64 = FALSE; } +#define run_in_process( a, b ) run_in_process_( __FILE__, __LINE__, a, b ) +static void run_in_process_( const char *file, int line, char **argv, const char *args ) +{ + STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; + PROCESS_INFORMATION info = {0}; + char cmdline[MAX_PATH * 2]; + DWORD ret; + + sprintf( cmdline, "%s %s %s", argv[0], argv[1], args ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); + ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); + if (!ret) return; + + wait_child_process( info.hProcess ); + CloseHandle( info.hThread ); + CloseHandle( info.hProcess ); +} + static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam ) { UINT message; @@ -1471,13 +1489,10 @@ static void test_mouse_ll_hook(void) SetCursorPos(pt_org.x, pt_org.y); } -static void test_GetMouseMovePointsEx(const char *argv0) +static void test_GetMouseMovePointsEx( char **argv ) { #define BUFLIM 64 #define MYERROR 0xdeadbeef - PROCESS_INFORMATION process_info; - STARTUPINFOA startup_info; - char path[MAX_PATH]; int i, count, retval; MOUSEMOVEPOINT in; MOUSEMOVEPOINT out[200]; @@ -1695,16 +1710,7 @@ static void test_GetMouseMovePointsEx(const char *argv0) retval = pGetMouseMovePointsEx( sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_HIGH_RESOLUTION_POINTS ); todo_wine ok( retval == 64, "expected to get 64 high resolution mouse move points but got %d\n", retval ); - sprintf(path, "%s input get_mouse_move_points_test", argv0); - memset(&startup_info, 0, sizeof(startup_info)); - startup_info.cb = sizeof(startup_info); - startup_info.dwFlags = STARTF_USESHOWWINDOW; - startup_info.wShowWindow = SW_SHOWNORMAL; - retval = CreateProcessA(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info ); - ok(retval, "CreateProcess \"%s\" failed err %lu.\n", path, GetLastError()); - winetest_wait_child_process(process_info.hProcess); - CloseHandle(process_info.hProcess); - CloseHandle(process_info.hThread); + run_in_process( argv, "test_GetMouseMovePointsEx_process" ); #undef BUFLIM #undef MYERROR } @@ -5021,11 +5027,13 @@ static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled ) ok( ret, "UnregisterClassW failed: %lu\n", GetLastError() ); } -static void test_EnableMouseInPointer_process( const char *arg ) +static void test_EnableMouseInPointer( const char *arg ) { DWORD enable = strtoul( arg, 0, 10 ); BOOL ret; + winetest_push_context( "enable %lu", enable ); + ret = pEnableMouseInPointer( enable ); todo_wine ok( ret, "EnableMouseInPointer failed, error %lu\n", GetLastError() ); @@ -5047,23 +5055,8 @@ static void test_EnableMouseInPointer_process( const char *arg ) ok( ret == enable, "IsMouseInPointerEnabled returned %u, error %lu\n", ret, GetLastError() ); test_GetPointerInfo( enable ); -} - -static void test_EnableMouseInPointer( char **argv, BOOL enable ) -{ - STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; - PROCESS_INFORMATION info = {0}; - char cmdline[MAX_PATH * 2]; - BOOL ret; - sprintf( cmdline, "%s %s EnableMouseInPointer %u", argv[0], argv[1], enable ); - ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); - ok( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); - if (!ret) return; - - wait_child_process( info.hProcess ); - CloseHandle( info.hThread ); - CloseHandle( info.hProcess ); + winetest_pop_context(); } START_TEST(input) @@ -5076,25 +5069,12 @@ START_TEST(input) GetCursorPos( &pos ); argc = winetest_get_mainargs(&argv); - if (argc >= 3 && strcmp(argv[2], "rawinput_test") == 0) - { - rawinput_test_process(); - return; - } - - if (argc >= 3 && strcmp(argv[2], "get_mouse_move_points_test") == 0) - { - test_GetMouseMovePointsEx_process(); - return; - } - - if (argc >= 4 && strcmp( argv[2], "EnableMouseInPointer" ) == 0) - { - winetest_push_context( "enable %s", argv[3] ); - test_EnableMouseInPointer_process( argv[3] ); - winetest_pop_context(); - return; - } + if (argc >= 3 && !strcmp( argv[2], "rawinput_test" )) + return rawinput_test_process(); + if (argc >= 3 && !strcmp( argv[2], "test_GetMouseMovePointsEx_process" )) + return test_GetMouseMovePointsEx_process(); + if (argc >= 4 && !strcmp( argv[2], "test_EnableMouseInPointer" )) + return test_EnableMouseInPointer( argv[3] ); test_SendInput(); test_Input_blackbox(); @@ -5120,7 +5100,7 @@ START_TEST(input) test_DefRawInputProc(); if(pGetMouseMovePointsEx) - test_GetMouseMovePointsEx(argv[0]); + test_GetMouseMovePointsEx( argv ); else win_skip("GetMouseMovePointsEx is not available\n"); @@ -5147,7 +5127,7 @@ START_TEST(input) win_skip( "EnableMouseInPointer not found, skipping tests\n" ); else { - test_EnableMouseInPointer( argv, FALSE ); - test_EnableMouseInPointer( argv, TRUE ); + run_in_process( argv, "test_EnableMouseInPointer 0" ); + run_in_process( argv, "test_EnableMouseInPointer 1" ); } } From 11e0acb3536aa73d2f52e5ea3e02d1a7619ce185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 14:42:16 +0200 Subject: [PATCH 503/758] user32/tests: Test more ClipCursor reset scenarios. (cherry picked from commit 0a33018211ab50620923943876994266c06cfe3e) CW-Bug-Id: #21879 --- dlls/user32/tests/input.c | 294 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 72e8070da52..fea207cf435 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -180,6 +180,34 @@ static void init_function_pointers(void) is_wow64 = FALSE; } +static const char *debugstr_ok( const char *cond ) +{ + int c, n = 0; + /* skip possible casts */ + while ((c = *cond++)) + { + if (c == '(') n++; + if (!n) break; + if (c == ')') n--; + } + if (!strchr( cond - 1, '(' )) return wine_dbg_sprintf( "got %s", cond - 1 ); + return wine_dbg_sprintf( "%.*s returned", (int)strcspn( cond - 1, "( " ), cond - 1 ); +} + +#define ok_eq( e, r, t, f, ... ) \ + do \ + { \ + t v = (r); \ + ok( v == (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ + } while (0) +#define ok_rect( e, r ) \ + do \ + { \ + RECT v = (r); \ + ok( EqualRect( &v, &(e) ), "%s %s\n", debugstr_ok(#r), wine_dbgstr_rect(&v) ); \ + } while (0) +#define ok_ret( e, r ) ok_eq( e, r, UINT_PTR, "%Iu, error %ld", GetLastError() ) + #define run_in_process( a, b ) run_in_process_( __FILE__, __LINE__, a, b ) static void run_in_process_( const char *file, int line, char **argv, const char *args ) { @@ -198,6 +226,72 @@ static void run_in_process_( const char *file, int line, char **argv, const char CloseHandle( info.hProcess ); } +#define run_in_desktop( a, b, c ) run_in_desktop_( __FILE__, __LINE__, a, b, c ) +static void run_in_desktop_( const char *file, int line, char **argv, + const char *args, BOOL input ) +{ + const char *desktop_name = "WineTest Desktop"; + STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; + PROCESS_INFORMATION info = {0}; + HDESK old_desktop, desktop; + char cmdline[MAX_PATH * 2]; + DWORD ret; + + old_desktop = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS ); + ok_(file, line)( !!old_desktop, "OpenInputDesktop failed, error %lu\n", GetLastError() ); + desktop = CreateDesktopA( desktop_name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); + ok_(file, line)( !!desktop, "CreateDesktopA failed, error %lu\n", GetLastError() ); + if (input) + { + ret = SwitchDesktop( desktop ); + ok_(file, line)( ret, "SwitchDesktop failed, error %lu\n", GetLastError() ); + } + + startup.lpDesktop = (char *)desktop_name; + sprintf( cmdline, "%s %s %s", argv[0], argv[1], args ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); + ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); + if (!ret) return; + + wait_child_process( info.hProcess ); + CloseHandle( info.hThread ); + CloseHandle( info.hProcess ); + + if (input) + { + ret = SwitchDesktop( old_desktop ); + ok_(file, line)( ret, "SwitchDesktop failed, error %lu\n", GetLastError() ); + } + ret = CloseDesktop( desktop ); + ok_(file, line)( ret, "CloseDesktop failed, error %lu\n", GetLastError() ); + ret = CloseDesktop( old_desktop ); + ok_(file, line)( ret, "CloseDesktop failed, error %lu\n", GetLastError() ); +} + +#define msg_wait_for_events( a, b, c ) msg_wait_for_events_( __FILE__, __LINE__, a, b, c ) +static DWORD msg_wait_for_events_( const char *file, int line, DWORD count, HANDLE *events, DWORD timeout ) +{ + DWORD ret, end = GetTickCount() + min( timeout, 5000 ); + MSG msg; + + while ((ret = MsgWaitForMultipleObjects( count, events, FALSE, min( timeout, 5000 ), QS_ALLINPUT )) <= count) + { + while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + if (ret < count) return ret; + if (timeout >= 5000) continue; + if (end <= GetTickCount()) timeout = 0; + else timeout = end - GetTickCount(); + } + + if (timeout >= 5000) ok_(file, line)( 0, "MsgWaitForMultipleObjects returned %#lx\n", ret ); + else ok_(file, line)( ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %#lx\n", ret ); + return ret; +} + static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam ) { UINT message; @@ -5059,6 +5153,198 @@ static void test_EnableMouseInPointer( const char *arg ) winetest_pop_context(); } +static BOOL CALLBACK get_virtual_screen_proc( HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM lp ) +{ + RECT *virtual_rect = (RECT *)lp; + UnionRect( virtual_rect, virtual_rect, rect ); + return TRUE; +} + +RECT get_virtual_screen_rect(void) +{ + RECT rect = {0}; + EnumDisplayMonitors( 0, NULL, get_virtual_screen_proc, (LPARAM)&rect ); + return rect; +} + +static void test_ClipCursor_dirty( const char *arg ) +{ + RECT rect, expect_rect = {1, 2, 3, 4}; + + /* check leaked clip rect from another desktop or process */ + ok_ret( 1, GetClipCursor( &rect ) ); + todo_wine_if( !strcmp( arg, "desktop" ) ) + ok_rect( expect_rect, rect ); + + /* intentionally leaking clipping rect */ +} + +static DWORD CALLBACK test_ClipCursor_thread( void *arg ) +{ + RECT rect, clip_rect, virtual_rect = get_virtual_screen_rect(); + HWND hwnd; + + clip_rect.left = clip_rect.right = (virtual_rect.left + virtual_rect.right) / 2; + clip_rect.top = clip_rect.bottom = (virtual_rect.top + virtual_rect.bottom) / 2; + + /* creating a window doesn't reset clipping rect */ + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, + NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* setting a window foreground does, even from the same process */ + ok_ret( 1, SetForegroundWindow( hwnd ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( virtual_rect, rect ); + + /* destroying the window doesn't reset the clipping rect */ + InflateRect( &clip_rect, +1, +1 ); + ok_ret( 1, ClipCursor( &clip_rect ) ); + ok_ret( 1, DestroyWindow( hwnd ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* intentionally leaking clipping rect */ + return 0; +} + +static void test_ClipCursor_process(void) +{ + RECT rect, clip_rect, virtual_rect = get_virtual_screen_rect(); + HWND hwnd, tmp_hwnd; + HANDLE thread; + + clip_rect.left = clip_rect.right = (virtual_rect.left + virtual_rect.right) / 2; + clip_rect.top = clip_rect.bottom = (virtual_rect.top + virtual_rect.bottom) / 2; + + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* creating an invisible window doesn't reset clip cursor */ + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, + NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + ok_ret( 1, DestroyWindow( hwnd ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* setting a window foreground, even invisible, resets it */ + hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, + NULL, NULL, NULL, NULL ); + ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + ok_ret( 1, SetForegroundWindow( hwnd ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( virtual_rect, rect ); + + ok_ret( 1, ClipCursor( &clip_rect ) ); + + /* creating and setting another window foreground doesn't reset it */ + tmp_hwnd = CreateWindowW( L"static", NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, + NULL, NULL, NULL, NULL ); + ok( !!tmp_hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + ok_ret( 1, SetForegroundWindow( tmp_hwnd ) ); + ok_ret( 1, DestroyWindow( tmp_hwnd ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* but changing foreground to another thread in the same process reset it */ + thread = CreateThread( NULL, 0, test_ClipCursor_thread, NULL, 0, NULL ); + ok( !!thread, "CreateThread failed, error %lu\n", GetLastError() ); + msg_wait_for_events( 1, &thread, 5000 ); + + /* thread exit and foreground window destruction doesn't reset the clipping rect */ + InflateRect( &clip_rect, +1, +1 ); + ok_ret( 1, DestroyWindow( hwnd ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + todo_wine + ok_rect( clip_rect, rect ); + + /* intentionally leaking clipping rect */ +} + +static void test_ClipCursor_desktop( char **argv ) +{ + RECT rect, clip_rect, virtual_rect = get_virtual_screen_rect(); + + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( virtual_rect, rect ); + + /* ClipCursor clips rectangle to the virtual screen rect */ + clip_rect = virtual_rect; + InflateRect( &clip_rect, +1, +1 ); + ok_ret( 1, ClipCursor( &clip_rect ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( virtual_rect, rect ); + + clip_rect = virtual_rect; + InflateRect( &clip_rect, -1, -1 ); + ok_ret( 1, ClipCursor( &clip_rect ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* ClipCursor(NULL) resets to the virtual screen rect */ + ok_ret( 1, ClipCursor( NULL ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( virtual_rect, rect ); + + clip_rect.left = clip_rect.right = (virtual_rect.left + virtual_rect.right) / 2; + clip_rect.top = clip_rect.bottom = (virtual_rect.top + virtual_rect.bottom) / 2; + ok_ret( 1, ClipCursor( &clip_rect ) ); + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* ClipCursor rejects invalid rectangles */ + clip_rect.right -= 1; + clip_rect.bottom -= 1; + SetLastError( 0xdeadbeef ); + ok_ret( 0, ClipCursor( &clip_rect ) ); + todo_wine + ok_ret( ERROR_ACCESS_DENIED, GetLastError() ); + + /* which doesn't reset the previous clip rect */ + clip_rect.right += 1; + clip_rect.bottom += 1; + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* running a process causes it to leak until foreground actually changes */ + run_in_process( argv, "test_ClipCursor_process" ); + + /* as foreground window is now transient, cursor clipping isn't reset */ + InflateRect( &clip_rect, +1, +1 ); + ok_ret( 1, GetClipCursor( &rect ) ); + todo_wine + ok_rect( clip_rect, rect ); + + /* intentionally leaking clipping rect */ +} + +static void test_ClipCursor( char **argv ) +{ + RECT rect, clip_rect = {1, 2, 3, 4}, virtual_rect = get_virtual_screen_rect(); + + ok_ret( 1, ClipCursor( &clip_rect ) ); + + /* running a new process doesn't reset clipping rectangle */ + run_in_process( argv, "test_ClipCursor_dirty process" ); + + /* running in a separate desktop, without switching desktop as well */ + run_in_desktop( argv, "test_ClipCursor_dirty desktop", 0 ); + + ok_ret( 1, GetClipCursor( &rect ) ); + ok_rect( clip_rect, rect ); + + /* running in a desktop and switching input resets the clipping rect */ + run_in_desktop( argv, "test_ClipCursor_desktop", 1 ); + + ok_ret( 1, GetClipCursor( &rect ) ); + todo_wine + ok_rect( virtual_rect, rect ); + if (!EqualRect( &rect, &virtual_rect )) ok_ret( 1, ClipCursor( NULL ) ); +} + START_TEST(input) { char **argv; @@ -5075,6 +5361,12 @@ START_TEST(input) return test_GetMouseMovePointsEx_process(); if (argc >= 4 && !strcmp( argv[2], "test_EnableMouseInPointer" )) return test_EnableMouseInPointer( argv[3] ); + if (argc >= 4 && !strcmp( argv[2], "test_ClipCursor_dirty" )) + return test_ClipCursor_dirty( argv[3] ); + if (argc >= 3 && !strcmp( argv[2], "test_ClipCursor_process" )) + return test_ClipCursor_process(); + if (argc >= 3 && !strcmp( argv[2], "test_ClipCursor_desktop" )) + return test_ClipCursor_desktop( argv ); test_SendInput(); test_Input_blackbox(); @@ -5130,4 +5422,6 @@ START_TEST(input) run_in_process( argv, "test_EnableMouseInPointer 0" ); run_in_process( argv, "test_EnableMouseInPointer 1" ); } + + test_ClipCursor( argv ); } From 25acae0eb3a02527a14a06e5418b977b7358ab08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 19:50:57 +0200 Subject: [PATCH 504/758] win32u: Reset cursor clipping rectangle on display mode change. (cherry picked from commit 18cb55583a50aabf60467316dd23b7c2763ea271) CW-Bug-Id: #21879 --- dlls/win32u/sysparams.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index aeae43921e2..4c8a82e9442 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2699,6 +2699,7 @@ static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmod if (!adapter_get_current_settings( adapter, ¤t_mode )) WARN( "Failed to get primary adapter current display settings.\n" ); adapter_release( adapter ); + NtUserClipCursor( NULL ); send_notify_message( NtUserGetDesktopWindow(), WM_DISPLAYCHANGE, current_mode.dmBitsPerPel, MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), FALSE ); send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel, From 16b651d36b5bb1f6a8d4f038b73557fb6cca758b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 19:50:57 +0200 Subject: [PATCH 505/758] winex11: Rely on win32u to reset clipping on display mode change. (cherry picked from commit 5bdbbee6f4a874159f2e527f39f071547c513ee4) CW-Bug-Id: #21879 --- dlls/winex11.drv/desktop.c | 1 - dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/x11drv.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index 66cbee171a5..f0c038d8981 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -448,7 +448,6 @@ void X11DRV_resize_desktop(void) NtUserSetWindowPos( hwnd, 0, virtual_rect.left, virtual_rect.top, virtual_rect.right - virtual_rect.left, virtual_rect.bottom - virtual_rect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE ); - ungrab_clipping_window(); /* HACK: always send the desktop resize notification, to eventually update fshack on windows */ send_message_timeout( HWND_BROADCAST, WM_X11DRV_DESKTOP_RESIZED, old_virtual_rect.left, diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index faa3aacdd3f..81aa9294baa 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -527,7 +527,7 @@ static BOOL grab_clipping_window( const RECT *clip ) * * Release the pointer grab on the clip window. */ -void ungrab_clipping_window(void) +static void ungrab_clipping_window(void) { Display *display = thread_init_display(); Window clip_window = init_clip_window(); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index a2e1998be8d..faf236e253c 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -740,7 +740,6 @@ extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; extern LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) DECLSPEC_HIDDEN; extern LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) DECLSPEC_HIDDEN; -extern void ungrab_clipping_window(void) DECLSPEC_HIDDEN; extern void reset_clipping_window(void) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; From 4420eeddc38b7e7f8e263c348533abc8656068d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 19:53:23 +0200 Subject: [PATCH 506/758] winex11: Reset clipping by calling NtUserClipCursor directly. (cherry picked from commit b863d3ab141a5243c6024a75185f9869b2f4e667) CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 4 ++-- dlls/winex11.drv/mouse.c | 11 ----------- dlls/winex11.drv/window.c | 2 +- dlls/winex11.drv/x11drv.h | 1 - 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 72eb2cfe443..9c518176e1e 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -946,7 +946,7 @@ static void focus_out( Display *display , HWND hwnd ) if (is_virtual_desktop()) { - if (hwnd == NtUserGetDesktopWindow()) reset_clipping_window(); + if (hwnd == NtUserGetDesktopWindow()) NtUserClipCursor( NULL ); return; } if (hwnd != NtUserGetForegroundWindow()) return; @@ -990,7 +990,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) { - if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window(); + if (!hwnd && event->window == x11drv_thread_data()->clip_window) NtUserClipCursor( NULL ); return TRUE; } if (!hwnd) return FALSE; diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 81aa9294baa..29ead8aa562 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -541,17 +541,6 @@ static void ungrab_clipping_window(void) send_notify_message( NtUserGetDesktopWindow(), WM_X11DRV_CLIP_CURSOR_NOTIFY, 0, 0 ); } -/*********************************************************************** - * reset_clipping_window - * - * Forcibly reset the window clipping on external events. - */ -void reset_clipping_window(void) -{ - ungrab_clipping_window(); - NtUserClipCursor( NULL ); /* make sure the clip rectangle is reset too */ -} - /*********************************************************************** * retry_grab_clipping_window * diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index c8b52e16d97..59452f0efe5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3274,7 +3274,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags, { release_win_data( data ); unmap_window( hwnd ); - if (NtUserIsWindowRectFullScreen( &old_window_rect )) reset_clipping_window(); + if (NtUserIsWindowRectFullScreen( &old_window_rect )) NtUserClipCursor( NULL ); if (!(data = get_win_data( hwnd ))) return; } } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index faf236e253c..93dff142a69 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -740,7 +740,6 @@ extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; extern LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) DECLSPEC_HIDDEN; extern LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) DECLSPEC_HIDDEN; -extern void reset_clipping_window(void) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; From f39488816062923be1be64d1df5c4880303d5039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 11:11:10 +0200 Subject: [PATCH 507/758] server: Don't reset cursor clipping on foreground thread exit. It will be reset on foreground input changes, when it happens. (cherry picked from commit 88cbc08b7fcb90c6048f5868c82e3c2165dcc1cd) CW-Bug-Id: #21879 --- dlls/user32/tests/input.c | 2 -- server/queue.c | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index fea207cf435..52418d9c864 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -5258,7 +5258,6 @@ static void test_ClipCursor_process(void) InflateRect( &clip_rect, +1, +1 ); ok_ret( 1, DestroyWindow( hwnd ) ); ok_ret( 1, GetClipCursor( &rect ) ); - todo_wine ok_rect( clip_rect, rect ); /* intentionally leaking clipping rect */ @@ -5315,7 +5314,6 @@ static void test_ClipCursor_desktop( char **argv ) /* as foreground window is now transient, cursor clipping isn't reset */ InflateRect( &clip_rect, +1, +1 ); ok_ret( 1, GetClipCursor( &rect ) ); - todo_wine ok_rect( clip_rect, rect ); /* intentionally leaking clipping rect */ diff --git a/server/queue.c b/server/queue.c index 79a57bad9db..5c9b4581cf1 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1294,12 +1294,13 @@ static void thread_input_dump( struct object *obj, int verbose ) static void thread_input_destroy( struct object *obj ) { struct thread_input *input = (struct thread_input *)obj; + struct desktop *desktop; empty_msg_list( &input->msg_list ); - if (input->desktop) + if ((desktop = input->desktop)) { - if (input->desktop->foreground_input == input) set_foreground_input( input->desktop, NULL ); - release_object( input->desktop ); + if (desktop->foreground_input == input) desktop->foreground_input = NULL; + release_object( desktop ); } release_object( input->shared_mapping ); } From 52115693954864cdde5f399438490e3cfbccbae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 29 May 2023 15:11:32 +0200 Subject: [PATCH 508/758] win32u: Add a reset parameter to WM_WINE_CLIPCURSOR and driver ClipCursor. (cherry picked from commit b7570b798eab0a6995d6c163ab04ef336311e52d) --- dlls/win32u/driver.c | 6 +++--- dlls/win32u/input.c | 10 +++++----- dlls/win32u/message.c | 2 +- dlls/win32u/win32u_private.h | 2 +- dlls/winemac.drv/macdrv.h | 3 +-- dlls/winemac.drv/mouse.c | 6 ++++-- dlls/winex11.drv/mouse.c | 13 +++++++------ dlls/winex11.drv/x11drv.h | 2 +- include/wine/gdi_driver.h | 2 +- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 7a2e4c3bcdd..3a96b45d03f 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -753,7 +753,7 @@ static BOOL nulldrv_SetCursorPos( INT x, INT y ) return TRUE; } -static BOOL nulldrv_ClipCursor( LPCRECT clip ) +static BOOL nulldrv_ClipCursor( const RECT *clip, BOOL reset ) { return TRUE; } @@ -1134,9 +1134,9 @@ static BOOL loaderdrv_SetCursorPos( INT x, INT y ) return load_driver()->pSetCursorPos( x, y ); } -static BOOL loaderdrv_ClipCursor( const RECT *clip ) +static BOOL loaderdrv_ClipCursor( const RECT *clip, BOOL reset ) { - return load_driver()->pClipCursor( clip ); + return load_driver()->pClipCursor( clip, reset ); } static LRESULT nulldrv_ClipboardWindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index e6a49ce7a1d..f56adb576d8 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2537,16 +2537,16 @@ BOOL get_clip_cursor( RECT *rect ) return TRUE; } -BOOL process_wine_clipcursor( BOOL empty ) +BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) { RECT rect; - TRACE( "empty %u\n", empty ); + TRACE( "empty %u, reset %u\n", empty, reset ); - if (empty) return user_driver->pClipCursor( NULL ); + if (empty || reset) return user_driver->pClipCursor( NULL, reset ); get_clip_cursor( &rect ); - return user_driver->pClipCursor( &rect ); + return user_driver->pClipCursor( &rect, FALSE ); } /*********************************************************************** @@ -2592,7 +2592,7 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) } } SERVER_END_REQ; - if (ret) user_driver->pClipCursor( &new_rect ); + if (ret) user_driver->pClipCursor( &new_rect, FALSE ); return ret; } diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index dead1ab3951..5b3cc86870e 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1287,7 +1287,7 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam ); } case WM_WINE_CLIPCURSOR: - return process_wine_clipcursor( wparam ); + return process_wine_clipcursor( wparam, lparam ); case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 286df9419a6..8cfa108e5b9 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -280,7 +280,7 @@ extern void toggle_caret( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL unregister_touch_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN; -extern BOOL process_wine_clipcursor( BOOL empty ) DECLSPEC_HIDDEN; +extern BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) DECLSPEC_HIDDEN; /* menu.c */ extern HMENU create_menu( BOOL is_popup ) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index d0fddcc0f21..39cb31bfdca 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -133,7 +133,7 @@ extern BOOL macdrv_UpdateDisplayDevices( const struct gdi_device_manager *device BOOL force, void *param ) DECLSPEC_HIDDEN; extern BOOL macdrv_GetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp) DECLSPEC_HIDDEN; extern BOOL macdrv_SetDeviceGammaRamp(PHYSDEV dev, LPVOID ramp) DECLSPEC_HIDDEN; -extern BOOL macdrv_ClipCursor(LPCRECT clip) DECLSPEC_HIDDEN; +extern BOOL macdrv_ClipCursor(const RECT *clip, BOOL reset) DECLSPEC_HIDDEN; extern LRESULT macdrv_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) DECLSPEC_HIDDEN; extern void macdrv_DestroyWindow(HWND hwnd) DECLSPEC_HIDDEN; extern void macdrv_SetDesktopWindow(HWND hwnd) DECLSPEC_HIDDEN; @@ -157,7 +157,6 @@ extern void macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags const RECT *visible_rect, const RECT *valid_rects, struct window_surface *surface) DECLSPEC_HIDDEN; extern void macdrv_DestroyCursorIcon(HCURSOR cursor) DECLSPEC_HIDDEN; -extern BOOL macdrv_ClipCursor(LPCRECT clip) DECLSPEC_HIDDEN; extern BOOL macdrv_GetCursorPos(LPPOINT pos) DECLSPEC_HIDDEN; extern void macdrv_SetCapture(HWND hwnd, UINT flags) DECLSPEC_HIDDEN; extern void macdrv_SetCursor(HCURSOR cursor) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c index 74c329488c4..9dfd1801fc9 100644 --- a/dlls/winemac.drv/mouse.c +++ b/dlls/winemac.drv/mouse.c @@ -660,11 +660,13 @@ void macdrv_DestroyCursorIcon(HCURSOR cursor) * * Set the cursor clipping rectangle. */ -BOOL macdrv_ClipCursor(LPCRECT clip) +BOOL macdrv_ClipCursor(const RECT *clip, BOOL reset) { CGRect rect; - TRACE("%s\n", wine_dbgstr_rect(clip)); + TRACE("%s %u\n", wine_dbgstr_rect(clip), reset); + + if (reset) return TRUE; if (clip) { diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 29ead8aa562..b6986a9d68c 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1643,17 +1643,18 @@ BOOL X11DRV_GetCursorPos(LPPOINT pos) /*********************************************************************** * ClipCursor (X11DRV.@) */ -BOOL X11DRV_ClipCursor( LPCRECT clip ) +BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) { - RECT virtual_rect = NtUserGetVirtualScreenRect(); + TRACE( "clip %p, reset %u\n", clip, reset ); - if (!clip) clip = &virtual_rect; - - if (grab_pointer) + if (!reset && grab_pointer) { + RECT virtual_rect = NtUserGetVirtualScreenRect(); HWND foreground = NtUserGetForegroundWindow(); DWORD tid, pid; + if (!clip) clip = &virtual_rect; + /* forward request to the foreground window if it's in a different thread */ tid = NtUserGetWindowThread( foreground, &pid ); if (tid && tid != GetCurrentThreadId() && pid == GetCurrentProcessId()) @@ -1701,7 +1702,7 @@ LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) else { NtUserGetClipCursor( &clip ); - X11DRV_ClipCursor( &clip ); + X11DRV_ClipCursor( &clip, FALSE ); } return 0; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 93dff142a69..4416f6403a1 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -218,7 +218,7 @@ extern void X11DRV_DestroyCursorIcon( HCURSOR handle ) DECLSPEC_HIDDEN; extern void X11DRV_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; extern BOOL X11DRV_SetCursorPos( INT x, INT y ) DECLSPEC_HIDDEN; extern BOOL X11DRV_GetCursorPos( LPPOINT pos ) DECLSPEC_HIDDEN; -extern BOOL X11DRV_ClipCursor( LPCRECT clip ) DECLSPEC_HIDDEN; +extern BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) DECLSPEC_HIDDEN; extern LONG X11DRV_ChangeDisplaySettings( LPDEVMODEW displays, LPCWSTR primary_name, HWND hwnd, DWORD flags, LPVOID lpvoid ) DECLSPEC_HIDDEN; extern BOOL X11DRV_GetCurrentDisplaySettings( LPCWSTR name, BOOL is_primary, LPDEVMODEW devmode ) DECLSPEC_HIDDEN; extern INT X11DRV_GetDisplayDepth( LPCWSTR name, BOOL is_primary ) DECLSPEC_HIDDEN; diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 29864a6eb47..510fdac6012 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -296,7 +296,7 @@ struct user_driver_funcs void (*pSetCursor)(HCURSOR); BOOL (*pGetCursorPos)(LPPOINT); BOOL (*pSetCursorPos)(INT,INT); - BOOL (*pClipCursor)(LPCRECT); + BOOL (*pClipCursor)(const RECT*,BOOL); /* clipboard functions */ LRESULT (*pClipboardWindowProc)(HWND,UINT,WPARAM,LPARAM); void (*pUpdateClipboard)(void); From 3ce74f365e1469b13b91a3cb91f3344e34764562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 10:50:41 +0200 Subject: [PATCH 509/758] server: Use the helper to reset the clip rect when the desktop size changes. (cherry picked from commit 2101d4d6ad64369e34f4e2d24de62447f677ea30) --- server/queue.c | 2 +- server/user.h | 1 + server/window.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/server/queue.c b/server/queue.c index 5c9b4581cf1..079310f0c16 100644 --- a/server/queue.c +++ b/server/queue.c @@ -539,7 +539,7 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig } /* set the cursor clip rectangle */ -static void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ) +void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ) { rectangle_t top_rect, new_rect; int x, y; diff --git a/server/user.h b/server/user.h index 1de900ae310..523a533ca4f 100644 --- a/server/user.h +++ b/server/user.h @@ -104,6 +104,7 @@ extern void queue_cleanup_window( struct thread *thread, user_handle_t win ); extern int init_thread_queue( struct thread *thread ); extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to ); extern void detach_thread_input( struct thread *thread_from ); +extern void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ); extern void post_message( user_handle_t win, unsigned int message, lparam_t wparam, lparam_t lparam ); extern void send_notify_message( user_handle_t win, unsigned int message, diff --git a/server/window.c b/server/window.c index 83aab43a583..260b44c07a8 100644 --- a/server/window.c +++ b/server/window.c @@ -1831,7 +1831,7 @@ static void set_window_pos( struct window *win, struct window *previous, } /* reset cursor clip rectangle when the desktop changes size */ - if (win == win->desktop->top_window) win->desktop->shared->cursor.clip = *window_rect; + if (win == win->desktop->top_window) set_clip_rectangle( win->desktop, NULL, 1 ); /* if the window is not visible, everything is easy */ if (!visible) return; From 3f7b0c2d5837e11d4c39fd4c1ac798beaca55b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 6 Jun 2023 09:53:55 +0200 Subject: [PATCH 510/758] server: Use a separate helper to merge WM_MOUSEMOVE messages. (cherry picked from commit bd06c87b5b9d495824769876cdcc64c5ebc2bd9b) --- server/queue.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/queue.c b/server/queue.c index 079310f0c16..477b85a54cf 100644 --- a/server/queue.c +++ b/server/queue.c @@ -721,14 +721,12 @@ static int merge_pointer_update_message( struct thread_input *input, const struc return 1; } -/* try to merge a message with the last in the list; return 1 if successful */ -static int merge_message( struct thread_input *input, const struct message *msg ) +/* try to merge a WM_MOUSEMOVE message with the last in the list; return 1 if successful */ +static int merge_mousemove( struct thread_input *input, const struct message *msg ) { struct message *prev; struct list *ptr; - if (msg->msg == WM_POINTERUPDATE) return merge_pointer_update_message( input, msg ); - if (msg->msg != WM_MOUSEMOVE) return 0; for (ptr = list_tail( &input->msg_list ); ptr; ptr = list_prev( &input->msg_list, ptr )) { prev = LIST_ENTRY( ptr, struct message, entry ); @@ -756,6 +754,14 @@ static int merge_message( struct thread_input *input, const struct message *msg return 1; } +/* try to merge a message with the messages in the list; return 1 if successful */ +static int merge_message( struct thread_input *input, const struct message *msg ) +{ + if (msg->msg == WM_POINTERUPDATE) return merge_pointer_update_message( input, msg ); + if (msg->msg == WM_MOUSEMOVE) return merge_mousemove( input, msg ); + return 0; +} + /* free a result structure */ static void free_result( struct message_result *result ) { From bd26ca4bbdbe79011726075e97dfea9480c168ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 2 Jun 2023 12:11:21 +0200 Subject: [PATCH 511/758] server: Queue a hardware WM_WINE_CLIPCURSOR message to the foreground thread. When applying a new cursor clipping rect, or to the previous foreground thread when foreground changes, to notify it of the cursor clipping rect being reset. (cherry picked from commit 5ebb1ed132d5261f410d7ff4f99c803f50752ed9) --- server/queue.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++--- server/user.h | 2 +- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/server/queue.c b/server/queue.c index 477b85a54cf..a820ce6d285 100644 --- a/server/queue.c +++ b/server/queue.c @@ -538,8 +538,25 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig *time = get_tick_count(); } +static void queue_clip_cursor_msg( struct desktop *desktop, lparam_t wparam, lparam_t lparam ) +{ + static const struct hw_msg_source source = { IMDT_UNAVAILABLE, IMO_SYSTEM }; + struct thread_input *input; + struct message *msg; + + if (!(msg = alloc_hardware_message( 0, source, get_tick_count(), 0 ))) return; + + msg->msg = WM_WINE_CLIPCURSOR; + msg->wparam = wparam; + msg->lparam = lparam; + msg->x = desktop->shared->cursor.x; + msg->y = desktop->shared->cursor.y; + if ((input = desktop->foreground_input)) msg->win = input->shared->active; + queue_hardware_message( desktop, msg, 1 ); +} + /* set the cursor clip rectangle */ -void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ) +void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int reset ) { rectangle_t top_rect, new_rect; int x, y; @@ -559,13 +576,17 @@ void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int s SHARED_WRITE_BEGIN( &desktop->shared->seq ); desktop->shared->cursor.clip = new_rect; - if (send_clip_msg) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, rect == NULL, 0 ); - /* warp the mouse to be inside the clip rect */ x = max( min( desktop->shared->cursor.x, desktop->shared->cursor.clip.right - 1 ), desktop->shared->cursor.clip.left ); y = max( min( desktop->shared->cursor.y, desktop->shared->cursor.clip.bottom - 1 ), desktop->shared->cursor.clip.top ); if (x != desktop->shared->cursor.x || y != desktop->shared->cursor.y) set_cursor_pos( desktop, x, y ); SHARED_WRITE_END( &desktop->shared->seq ); + + /* request clip cursor rectangle reset to the desktop thread */ + if (reset) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, TRUE, FALSE ); + + /* notify foreground thread, of reset, or to apply new cursor clipping rect */ + queue_clip_cursor_msg( desktop, rect == NULL, reset ); } /* change the foreground input and reset the cursor clip rect */ @@ -672,6 +693,7 @@ static inline int get_hardware_msg_bit( struct message *msg ) if (msg->msg >= WM_KEYFIRST && msg->msg <= WM_KEYLAST) return QS_KEY; if (msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || msg->msg == WM_POINTERUPDATE) return QS_POINTER; + if (msg->msg == WM_WINE_CLIPCURSOR) return QS_RAWINPUT; return QS_MOUSEBUTTON; } @@ -754,11 +776,37 @@ static int merge_mousemove( struct thread_input *input, const struct message *ms return 1; } +/* try to merge a WM_WINE_CLIPCURSOR message with the last in the list; return 1 if successful */ +static int merge_wine_clipcursor( struct thread_input *input, const struct message *msg ) +{ + struct message *prev; + + LIST_FOR_EACH_ENTRY_REV( prev, &input->msg_list, struct message, entry ) + if (prev->msg == WM_WINE_CLIPCURSOR) break; + if (&prev->entry == &input->msg_list) return 0; + + if (prev->result) return 0; + if (prev->win != msg->win) return 0; + if (prev->type != msg->type) return 0; + + /* now we can merge it */ + prev->wparam = msg->wparam; + prev->lparam = msg->lparam; + prev->x = msg->x; + prev->y = msg->y; + prev->time = msg->time; + list_remove( &prev->entry ); + list_add_tail( &input->msg_list, &prev->entry ); + + return 1; +} + /* try to merge a message with the messages in the list; return 1 if successful */ static int merge_message( struct thread_input *input, const struct message *msg ) { if (msg->msg == WM_POINTERUPDATE) return merge_pointer_update_message( input, msg ); if (msg->msg == WM_MOUSEMOVE) return merge_mousemove( input, msg ); + if (msg->msg == WM_WINE_CLIPCURSOR) return merge_wine_clipcursor( input, msg ); return 0; } diff --git a/server/user.h b/server/user.h index 523a533ca4f..4f6dc4755d5 100644 --- a/server/user.h +++ b/server/user.h @@ -104,7 +104,7 @@ extern void queue_cleanup_window( struct thread *thread, user_handle_t win ); extern int init_thread_queue( struct thread *thread ); extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to ); extern void detach_thread_input( struct thread *thread_from ); -extern void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int send_clip_msg ); +extern void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int reset ); extern void post_message( user_handle_t win, unsigned int message, lparam_t wparam, lparam_t lparam ); extern void send_notify_message( user_handle_t win, unsigned int message, From 0cfdbf533b50529f7ada5a498d274768d7a9ebfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 11:23:54 +0200 Subject: [PATCH 512/758] win32u: Asynchronously apply or reset ClipCursor from the hardware message. (cherry picked from commit 47e208e9ec91b3ac0aca7042778ff7d56c14ab4d) --- dlls/win32u/input.c | 2 +- dlls/win32u/message.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index f56adb576d8..ae8444ecd87 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2592,7 +2592,7 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) } } SERVER_END_REQ; - if (ret) user_driver->pClipCursor( &new_rect, FALSE ); + return ret; } diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 5b3cc86870e..8c0ba27efd5 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1867,6 +1867,8 @@ static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardwar ret = process_keyboard_message( msg, hw_id, hwnd_filter, first, last, remove ); else if (is_mouse_message( msg->message )) ret = process_mouse_message( msg, hw_id, msg_data->info, hwnd_filter, first, last, remove ); + else if (msg->message == WM_WINE_CLIPCURSOR) + process_wine_clipcursor( msg->wParam, msg->lParam ); else ERR( "unknown message type %x\n", msg->message ); SetThreadDpiAwarenessContext( context ); From aba3d9d5c6de3b8d56f6723662af973c31e93d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 26 May 2023 23:02:17 +0200 Subject: [PATCH 513/758] winex11: Remove now unnecessary ClipCursor forwarding to foreground thread. (cherry picked from commit 397cd1c0c00ce3dc62b0c32b234ede5a0f48aea0) --- dlls/winex11.drv/mouse.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index b6986a9d68c..5158eda7984 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1650,20 +1650,9 @@ BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) if (!reset && grab_pointer) { RECT virtual_rect = NtUserGetVirtualScreenRect(); - HWND foreground = NtUserGetForegroundWindow(); - DWORD tid, pid; if (!clip) clip = &virtual_rect; - /* forward request to the foreground window if it's in a different thread */ - tid = NtUserGetWindowThread( foreground, &pid ); - if (tid && tid != GetCurrentThreadId() && pid == GetCurrentProcessId()) - { - TRACE( "forwarding clip request to %p\n", foreground ); - send_notify_message( foreground, WM_X11DRV_CLIP_CURSOR_REQUEST, FALSE, FALSE ); - return TRUE; - } - /* we are clipping if the clip rectangle is smaller than the screen */ if (clip->left > virtual_rect.left || clip->right < virtual_rect.right || clip->top > virtual_rect.top || clip->bottom < virtual_rect.bottom) @@ -1675,8 +1664,8 @@ BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) struct x11drv_thread_data *data = x11drv_thread_data(); if (data && data->clip_hwnd) { - if (EqualRect( clip, &clip_rect ) || clip_fullscreen_window( foreground, TRUE )) - return TRUE; + if (EqualRect( clip, &clip_rect )) return TRUE; + if (clip_fullscreen_window( NtUserGetForegroundWindow(), TRUE )) return TRUE; } } } @@ -1691,20 +1680,12 @@ BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) */ LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) { - RECT clip; - if (hwnd == NtUserGetDesktopWindow()) WARN( "ignoring clip cursor request on desktop window.\n" ); else if (hwnd != NtUserGetForegroundWindow()) WARN( "ignoring clip cursor request on non-foreground window.\n" ); else if (fullscreen) clip_fullscreen_window( hwnd, reset ); - else - { - NtUserGetClipCursor( &clip ); - X11DRV_ClipCursor( &clip, FALSE ); - } - return 0; } From 9325fbf6f67ff8119fb0ac5b2adca5727867a745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 12 Jun 2023 23:29:42 +0200 Subject: [PATCH 514/758] server: Pass the message code to get_hardware_msg_bit. (cherry picked from commit 3ae2dc46484712cd2454b5aab4e82bd2c63d9ef4) --- server/queue.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/server/queue.c b/server/queue.c index a820ce6d285..befc113f84b 100644 --- a/server/queue.c +++ b/server/queue.c @@ -686,14 +686,14 @@ static inline int filter_contains_hw_range( unsigned int first, unsigned int las } /* get the QS_* bit corresponding to a given hardware message */ -static inline int get_hardware_msg_bit( struct message *msg ) -{ - if (msg->msg == WM_INPUT_DEVICE_CHANGE || msg->msg == WM_INPUT) return QS_RAWINPUT; - if (msg->msg == WM_MOUSEMOVE || msg->msg == WM_NCMOUSEMOVE) return QS_MOUSEMOVE; - if (msg->msg >= WM_KEYFIRST && msg->msg <= WM_KEYLAST) return QS_KEY; - if (msg->msg == WM_POINTERDOWN || msg->msg == WM_POINTERUP || - msg->msg == WM_POINTERUPDATE) return QS_POINTER; - if (msg->msg == WM_WINE_CLIPCURSOR) return QS_RAWINPUT; +static inline int get_hardware_msg_bit( unsigned int message ) +{ + if (message == WM_INPUT_DEVICE_CHANGE || message == WM_INPUT) return QS_RAWINPUT; + if (message == WM_MOUSEMOVE || message == WM_NCMOUSEMOVE) return QS_MOUSEMOVE; + if (message >= WM_KEYFIRST && message <= WM_KEYLAST) return QS_KEY; + if (message == WM_POINTERDOWN || message == WM_POINTERUP || + message == WM_POINTERUPDATE) return QS_POINTER; + if (message == WM_WINE_CLIPCURSOR) return QS_RAWINPUT; return QS_MOUSEBUTTON; } @@ -1728,10 +1728,10 @@ static void release_hardware_message( struct msg_queue *queue, unsigned int hw_i if (&msg->entry == &input->msg_list) return; /* not found */ /* clear the queue bit for that message */ - clr_bit = get_hardware_msg_bit( msg ); + clr_bit = get_hardware_msg_bit( msg->msg ); LIST_FOR_EACH_ENTRY( other, &input->msg_list, struct message, entry ) { - if (other != msg && get_hardware_msg_bit( other ) == clr_bit) + if (other != msg && get_hardware_msg_bit( other->msg ) == clr_bit) { clr_bit = 0; break; @@ -1790,7 +1790,7 @@ static user_handle_t find_hardware_message_window( struct desktop *desktop, stru *thread = NULL; *msg_code = msg->msg; - switch (get_hardware_msg_bit( msg )) + switch (get_hardware_msg_bit( msg->msg )) { case QS_POINTER: case QS_RAWINPUT: @@ -1855,7 +1855,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg last_input_time = get_tick_count(); if (msg->msg != WM_MOUSEMOVE) always_queue = 1; - switch (get_hardware_msg_bit( msg )) + switch (get_hardware_msg_bit( msg->msg )) { case QS_KEY: if (queue_hotkey_message( desktop, msg )) return; @@ -1911,7 +1911,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg { msg->unique_id = 0; /* will be set once we return it to the app */ list_add_tail( &input->msg_list, &msg->entry ); - set_queue_bits( thread->queue, get_hardware_msg_bit(msg) ); + set_queue_bits( thread->queue, get_hardware_msg_bit( msg->msg ) ); } release_object( thread ); } @@ -2525,7 +2525,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user if (win_thread->queue->input == input) { /* wake the other thread */ - set_queue_bits( win_thread->queue, get_hardware_msg_bit(msg) ); + set_queue_bits( win_thread->queue, get_hardware_msg_bit( msg->msg ) ); got_one = 1; } else @@ -2544,7 +2544,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user * match the filter we skip it */ if (got_one || !check_hw_message_filter( win, msg_code, filter_win, first, last )) { - clear_bits &= ~get_hardware_msg_bit( msg ); + clear_bits &= ~get_hardware_msg_bit( msg->msg ); continue; } @@ -2568,7 +2568,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user data->hw_id = msg->unique_id; set_reply_data( msg->data, msg->data_size ); - if ((get_hardware_msg_bit( msg ) & (QS_POINTER | QS_RAWINPUT)) && (flags & PM_REMOVE)) + if ((get_hardware_msg_bit( msg->msg ) & (QS_POINTER | QS_RAWINPUT)) && (flags & PM_REMOVE)) release_hardware_message( current->queue, data->hw_id ); return 1; } From 6fa1762d89b6aa67c71592a5aca4d3481d6e304e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 10 Jun 2023 15:17:41 +0200 Subject: [PATCH 515/758] server: Use hardware message category when checking filter. (cherry picked from commit 6ac82b2a24a8dc9c4547ba896c9313c5864795a7) --- server/queue.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/queue.c b/server/queue.c index befc113f84b..abbe99e0042 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2444,19 +2444,19 @@ static void queue_custom_hardware_message( struct desktop *desktop, user_handle_ static int check_hw_message_filter( user_handle_t win, unsigned int msg_code, user_handle_t filter_win, unsigned int first, unsigned int last ) { - if (msg_code >= WM_KEYFIRST && msg_code <= WM_KEYLAST) + switch (get_hardware_msg_bit( msg_code )) { + case QS_KEY: /* we can only test the window for a keyboard message since the * dest window for a mouse message depends on hittest */ if (filter_win && win != filter_win && !is_child_window( filter_win, win )) return 0; /* the message code is final for a keyboard message, we can simply check it */ return check_msg_filter( msg_code, first, last ); - } - else /* mouse message */ - { - /* we need to check all possible values that the message can have in the end */ + case QS_MOUSEMOVE: + case QS_MOUSEBUTTON: + /* we need to check all possible values that the message can have in the end */ if (check_msg_filter( msg_code, first, last )) return 1; if (msg_code == WM_MOUSEWHEEL) return 0; /* no other possible value for this one */ @@ -2471,6 +2471,9 @@ static int check_hw_message_filter( user_handle_t win, unsigned int msg_code, if (check_msg_filter( msg_code + (WM_NCLBUTTONDBLCLK - WM_LBUTTONDOWN), first, last )) return 1; } return 0; + + default: + return check_msg_filter( msg_code, first, last ); } } From 1d7de24f737fda22f63d59bad6e8c5988095b512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 12 Jun 2023 10:37:56 +0200 Subject: [PATCH 516/758] imm32: Avoid resizing IMCC to zero-size buffer. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55027 (cherry picked from commit 5911f36cfff1b48047042f2848b59a626258b29c) --- dlls/imm32/ime.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index e15367f5fa8..b1ff6c54c4e 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -366,7 +366,9 @@ static UINT ime_set_comp_string( HIMC himc, LPARAM lparam ) if (!(ctx = ImmLockIMC( himc ))) return 0; count = ImeToAsciiEx( VK_PROCESSKEY, lparam, NULL, &buffer.list, 0, himc ); - if (count >= buffer.uMsgCount) + if (!count) + TRACE( "ImeToAsciiEx returned no messages\n" ); + else if (count >= buffer.uMsgCount) WARN( "ImeToAsciiEx returned %#x messages\n", count ); else if (!(himcc = ImmReSizeIMCC( ctx->hMsgBuf, (ctx->dwNumMsgBuf + count) * sizeof(*msgs) ))) WARN( "Failed to resize input context message buffer\n" ); @@ -536,7 +538,7 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, if (!(ctx = ImmLockIMC( himc ))) return 0; if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) goto done; - size = compstr->dwSize; + size = max( compstr->dwSize, sizeof(COMPOSITIONSTRING) ); do { From a0f7e1d3b5d6e86ea1dca2673e6c90f8d05635cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 12 Jun 2023 23:30:42 +0200 Subject: [PATCH 517/758] winex11: Remove now unnecessary WM_X11DRV_CLIP_CURSOR_NOTIFY. (cherry picked from commit bb043fb63eeec5346588762178f0bae8be0f51a6) --- dlls/winex11.drv/mouse.c | 49 ++++++--------------------------------- dlls/winex11.drv/window.c | 2 -- dlls/winex11.drv/x11drv.h | 2 -- 3 files changed, 7 insertions(+), 46 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 5158eda7984..b2c82b00a67 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -514,7 +514,6 @@ static BOOL grab_clipping_window( const RECT *clip ) if (!data->clip_hwnd) sync_window_cursor( clip_window ); InterlockedExchangePointer( (void **)&cursor_window, msg_hwnd ); data->clip_hwnd = msg_hwnd; - send_notify_message( NtUserGetDesktopWindow(), WM_X11DRV_CLIP_CURSOR_NOTIFY, 0, (LPARAM)msg_hwnd ); return TRUE; #else WARN( "XInput2 was not available at compile time\n" ); @@ -529,16 +528,19 @@ static BOOL grab_clipping_window( const RECT *clip ) */ static void ungrab_clipping_window(void) { - Display *display = thread_init_display(); + struct x11drv_thread_data *data = x11drv_init_thread_data(); Window clip_window = init_clip_window(); if (!clip_window) return; TRACE( "no longer clipping\n" ); - XUnmapWindow( display, clip_window ); - if (clipping_cursor) XUngrabPointer( display, CurrentTime ); + XUnmapWindow( data->display, clip_window ); + if (clipping_cursor) XUngrabPointer( data->display, CurrentTime ); clipping_cursor = FALSE; - send_notify_message( NtUserGetDesktopWindow(), WM_X11DRV_CLIP_CURSOR_NOTIFY, 0, 0 ); + NtUserDestroyWindow( data->clip_hwnd ); + data->clip_hwnd = 0; + data->clip_reset = NtGetTickCount(); + X11DRV_XInput2_Enable( data->display, None, 0 ); } /*********************************************************************** @@ -555,43 +557,6 @@ void retry_grab_clipping_window(void) NtUserClipCursor( &last_clip_rect ); } -/*********************************************************************** - * clip_cursor_notify - * - * Notification function called upon receiving a WM_X11DRV_CLIP_CURSOR_NOTIFY. - */ -LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) -{ - struct x11drv_thread_data *data = x11drv_init_thread_data(); - - if (hwnd == NtUserGetDesktopWindow()) /* change the clip window stored in the desktop process */ - { - static HWND clip_hwnd; - - HWND prev = clip_hwnd; - clip_hwnd = new_clip_hwnd; - if (prev || new_clip_hwnd) TRACE( "clip hwnd changed from %p to %p\n", prev, new_clip_hwnd ); - if (prev) send_notify_message( prev, WM_X11DRV_CLIP_CURSOR_NOTIFY, (WPARAM)prev, 0 ); - } - else if (hwnd == data->clip_hwnd) /* this is a notification that clipping has been reset */ - { - TRACE( "clip hwnd reset from %p\n", hwnd ); - data->clip_hwnd = 0; - data->clip_reset = NtGetTickCount(); - X11DRV_XInput2_Enable( data->display, None, 0 ); - NtUserDestroyWindow( hwnd ); - } - else if (prev_clip_hwnd) - { - /* This is a notification send by the desktop window to an old - * dangling clip window. - */ - TRACE( "destroying old clip hwnd %p\n", prev_clip_hwnd ); - NtUserDestroyWindow( prev_clip_hwnd ); - } - return 0; -} - /*********************************************************************** * clip_fullscreen_window * diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 59452f0efe5..53b22db4695 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3742,8 +3742,6 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) } return 0; } - case WM_X11DRV_CLIP_CURSOR_NOTIFY: - return clip_cursor_notify( hwnd, (HWND)wp, (HWND)lp ); case WM_X11DRV_CLIP_CURSOR_REQUEST: return clip_cursor_request( hwnd, (BOOL)wp, (BOOL)lp ); case WM_X11DRV_DELETE_TAB: diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 4416f6403a1..6dd4077fccc 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -609,7 +609,6 @@ enum x11drv_window_messages WM_X11DRV_SET_WIN_REGION, WM_X11DRV_DESKTOP_RESIZED, WM_X11DRV_SET_CURSOR, - WM_X11DRV_CLIP_CURSOR_NOTIFY, WM_X11DRV_CLIP_CURSOR_REQUEST, WM_X11DRV_DELETE_TAB, WM_X11DRV_ADD_TAB @@ -738,7 +737,6 @@ extern XContext cursor_context DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; -extern LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_hwnd, HWND new_clip_hwnd ) DECLSPEC_HIDDEN; extern LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; From 66f993110f42f648446db3f5534b5a41413314fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 12 Jun 2023 23:31:26 +0200 Subject: [PATCH 518/758] win32u: Move grab_pointer registry option from winex11. (cherry picked from commit f47ed2926037b96942bd40f6140601987a3025cb) --- dlls/win32u/input.c | 7 +++- dlls/win32u/sysparams.c | 75 +++++++++++++++++++++++++++++++++- dlls/win32u/win32u_private.h | 1 + dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/x11drv.h | 1 - dlls/winex11.drv/x11drv_main.c | 4 -- 6 files changed, 81 insertions(+), 9 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index ae8444ecd87..b150a656b82 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -405,6 +405,8 @@ static const KBDTABLES kbdus_tables = }; +BOOL grab_pointer = TRUE; + static void kbd_tables_init_vsc2vk( const KBDTABLES *tables, BYTE vsc2vk[0x300] ) { const VSC_VK *entry; @@ -2543,7 +2545,10 @@ BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) TRACE( "empty %u, reset %u\n", empty, reset ); - if (empty || reset) return user_driver->pClipCursor( NULL, reset ); + if (reset) return user_driver->pClipCursor( NULL, TRUE ); + + if (!grab_pointer) return TRUE; + if (empty) return user_driver->pClipCursor( NULL, reset ); get_clip_cursor( &rect ); return user_driver->pClipCursor( &rect, FALSE ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 4c8a82e9442..0f730dcac52 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -4100,13 +4100,47 @@ static union sysparam_all_entry * const default_entries[] = (union sysparam_all_entry *)&entry_AUDIODESC_ON, }; -void sysparams_init(void) +/*********************************************************************** + * get_config_key + * + * Get a config key from either the app-specific or the default config + */ +static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name, + WCHAR *buffer, DWORD size ) { + WCHAR nameW[128]; + char buf[2048]; + KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf; + + asciiz_to_unicode( nameW, name ); + + if (appkey && query_reg_value( appkey, nameW, info, sizeof(buf) )) + { + size = min( info->DataLength, size - sizeof(WCHAR) ); + memcpy( buffer, info->Data, size ); + buffer[size / sizeof(WCHAR)] = 0; + return 0; + } + + if (defkey && query_reg_value( defkey, nameW, info, sizeof(buf) )) + { + size = min( info->DataLength, size - sizeof(WCHAR) ); + memcpy( buffer, info->Data, size ); + buffer[size / sizeof(WCHAR)] = 0; + return 0; + } + + return ERROR_FILE_NOT_FOUND; +} +void sysparams_init(void) +{ + WCHAR buffer[MAX_PATH+16], *p, *appname; DWORD i, dispos, dpi_scaling; WCHAR layout[KL_NAMELENGTH]; pthread_mutexattr_t attr; - HKEY hkey; + HKEY hkey, appkey = 0; + DWORD len; static const WCHAR software_wineW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e'}; static const WCHAR temporary_system_parametersW[] = @@ -4115,6 +4149,7 @@ void sysparams_init(void) static const WCHAR oneW[] = {'1',0}; static const WCHAR kl_preloadW[] = {'K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','\\','P','r','e','l','o','a','d'}; + static const WCHAR x11driverW[] = {'\\','X','1','1',' ','D','r','i','v','e','r',0}; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); @@ -4174,6 +4209,42 @@ void sysparams_init(void) for (i = 0; i < ARRAY_SIZE( default_entries ); i++) default_entries[i]->hdr.init( default_entries[i] ); } + + /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */ + hkey = reg_open_hkcu_key( "Software\\Wine\\X11 Driver" ); + + /* open the app-specific key */ + + appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; + if ((p = wcsrchr( appname, '/' ))) appname = p + 1; + if ((p = wcsrchr( appname, '\\' ))) appname = p + 1; + len = lstrlenW( appname ); + + if (len && len < MAX_PATH) + { + HKEY tmpkey; + int i; + + for (i = 0; appname[i]; i++) buffer[i] = RtlDowncaseUnicodeChar( appname[i] ); + buffer[i] = 0; + appname = buffer; + memcpy( appname + i, x11driverW, sizeof(x11driverW) ); + + /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */ + if ((tmpkey = reg_open_hkcu_key( "Software\\Wine\\AppDefaults" ))) + { + appkey = reg_open_key( tmpkey, appname, lstrlenW( appname ) * sizeof(WCHAR) ); + NtClose( tmpkey ); + } + } + +#define IS_OPTION_TRUE(ch) \ + ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1') + + if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) )) + grab_pointer = IS_OPTION_TRUE( buffer[0] ); + +#undef IS_OPTION_TRUE } static BOOL update_desktop_wallpaper(void) diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 8cfa108e5b9..7faa33f2aad 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -263,6 +263,7 @@ extern BOOL register_imm_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void unregister_imm_window( HWND hwnd ) DECLSPEC_HIDDEN; /* input.c */ +extern BOOL grab_pointer DECLSPEC_HIDDEN; extern BOOL destroy_caret(void) DECLSPEC_HIDDEN; extern BOOL enable_mouse_in_pointer DECLSPEC_HIDDEN; extern HWND get_active_window(void) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index b2c82b00a67..cefe42d1d69 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1612,7 +1612,7 @@ BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) { TRACE( "clip %p, reset %u\n", clip, reset ); - if (!reset && grab_pointer) + if (!reset) { RECT virtual_rect = NtUserGetVirtualScreenRect(); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 6dd4077fccc..ade0deb45da 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -446,7 +446,6 @@ extern BOOL use_take_focus DECLSPEC_HIDDEN; extern BOOL use_primary_selection DECLSPEC_HIDDEN; extern BOOL use_system_cursors DECLSPEC_HIDDEN; extern BOOL show_systray DECLSPEC_HIDDEN; -extern BOOL grab_pointer DECLSPEC_HIDDEN; extern BOOL grab_fullscreen DECLSPEC_HIDDEN; extern BOOL usexcomposite DECLSPEC_HIDDEN; extern BOOL use_xfixes DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 5e700df456a..3a0f2a0e48e 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -76,7 +76,6 @@ BOOL use_take_focus = FALSE; BOOL use_primary_selection = FALSE; BOOL use_system_cursors = TRUE; BOOL show_systray = TRUE; -BOOL grab_pointer = TRUE; BOOL grab_fullscreen = FALSE; BOOL managed_mode = TRUE; BOOL decorated_mode = TRUE; @@ -525,9 +524,6 @@ static void setup_options(void) if (!get_config_key( hkey, appkey, "ShowSystray", buffer, sizeof(buffer) )) show_systray = IS_OPTION_TRUE( buffer[0] ); - if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) )) - grab_pointer = IS_OPTION_TRUE( buffer[0] ); - if (!get_config_key( hkey, appkey, "GrabFullscreen", buffer, sizeof(buffer) )) grab_fullscreen = IS_OPTION_TRUE( buffer[0] ); From a93ec87cebc9455f8ad810d71ddb5635f81eb77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 12 Jun 2023 23:31:59 +0200 Subject: [PATCH 519/758] win32u: Add a clipping_reset member to user_thread_info. (cherry picked from commit 89a7c05ad91e7ba92326205593918fec2c75e7bc) --- dlls/win32u/input.c | 7 ++++++- dlls/win32u/ntuser_private.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index b150a656b82..12e64bd8bff 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2541,11 +2541,16 @@ BOOL get_clip_cursor( RECT *rect ) BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) { + struct user_thread_info *thread_info = get_user_thread_info(); RECT rect; TRACE( "empty %u, reset %u\n", empty, reset ); - if (reset) return user_driver->pClipCursor( NULL, TRUE ); + if (reset) + { + thread_info->clipping_reset = NtGetTickCount(); + return user_driver->pClipCursor( NULL, TRUE ); + } if (!grab_pointer) return TRUE; if (empty) return user_driver->pClipCursor( NULL, reset ); diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index b7d2b633eb5..56b74ce0ebc 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -140,6 +140,7 @@ struct user_thread_info struct rawinput_thread_data *rawinput; /* RawInput thread local data / buffer */ struct touchinput_thread_data *touchinput; /* touch input thread local buffer */ UINT spy_indent; /* Current spy indent */ + DWORD clipping_reset; /* time when clipping was last reset */ struct desktop_shared_memory *desktop_shared_memory; /* Ptr to server's desktop shared memory */ struct queue_shared_memory *queue_shared_memory; /* Ptr to server's thread queue shared memory */ struct input_shared_memory *input_shared_memory; /* Ptr to server's thread input shared memory */ From 5f784b5f75a43ca508734b3e7c01331f2b73b5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 18:43:48 +0200 Subject: [PATCH 520/758] win32u: Add a clipping_cursor member to user_thread_info. (cherry picked from commit 3cca65e3283d12d5b61b2ac9b314c36c30a3fa1d) --- dlls/win32u/input.c | 9 ++++++++- dlls/win32u/ntuser_private.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 12e64bd8bff..9530cba031b 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -404,6 +404,7 @@ static const KBDTABLES kbdus_tables = .fLocaleFlags = MAKELONG(0, KBD_VERSION), }; +static LONG clipping_cursor; /* clipping thread counter */ BOOL grab_pointer = TRUE; @@ -2546,6 +2547,9 @@ BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) TRACE( "empty %u, reset %u\n", empty, reset ); + if (thread_info->clipping_cursor) InterlockedDecrement( &clipping_cursor ); + thread_info->clipping_cursor = FALSE; + if (reset) { thread_info->clipping_reset = NtGetTickCount(); @@ -2556,7 +2560,10 @@ BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) if (empty) return user_driver->pClipCursor( NULL, reset ); get_clip_cursor( &rect ); - return user_driver->pClipCursor( &rect, FALSE ); + if (!user_driver->pClipCursor( &rect, FALSE )) return FALSE; + InterlockedIncrement( &clipping_cursor ); + thread_info->clipping_cursor = TRUE; + return TRUE; } /*********************************************************************** diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 56b74ce0ebc..3b49ff48f35 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -140,6 +140,7 @@ struct user_thread_info struct rawinput_thread_data *rawinput; /* RawInput thread local data / buffer */ struct touchinput_thread_data *touchinput; /* touch input thread local buffer */ UINT spy_indent; /* Current spy indent */ + BOOL clipping_cursor; /* thread is currently clipping */ DWORD clipping_reset; /* time when clipping was last reset */ struct desktop_shared_memory *desktop_shared_memory; /* Ptr to server's desktop shared memory */ struct queue_shared_memory *queue_shared_memory; /* Ptr to server's thread queue shared memory */ From 2af465e855f5a3666503fb2aa4b4dda17a3ac2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 31 May 2023 21:15:00 +0200 Subject: [PATCH 521/758] winex11: Move clip_fullscreen_window foreground check inside it. (cherry picked from commit b1d273bba3a5e02d78889e1798d22226d41bc0b7) --- dlls/winex11.drv/event.c | 2 +- dlls/winex11.drv/mouse.c | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 9c518176e1e..d7e68827cbc 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -893,7 +893,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) xim_set_focus( hwnd, TRUE ); if (use_take_focus) { - if (hwnd == NtUserGetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE ); + clip_fullscreen_window( hwnd, FALSE ); return TRUE; } diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index cefe42d1d69..781bd4fe575 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -572,6 +572,8 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) BOOL fullscreen; if (hwnd == NtUserGetDesktopWindow()) return FALSE; + if (hwnd != NtUserGetForegroundWindow()) return FALSE; + style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); if (!(style & WS_VISIBLE)) return FALSE; if ((style & (WS_POPUP | WS_CHILD)) == WS_CHILD) return FALSE; @@ -708,12 +710,8 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU last_cursor_change = input->u.mi.time; } - if (hwnd != NtUserGetDesktopWindow()) - { - hwnd = NtUserGetAncestor( hwnd, GA_ROOT ); - if ((input->u.mi.dwFlags & (MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_RIGHTDOWN)) && hwnd == NtUserGetForegroundWindow()) - clip_fullscreen_window( hwnd, FALSE ); - } + if (input->u.mi.dwFlags & (MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_RIGHTDOWN)) + clip_fullscreen_window( hwnd, FALSE ); /* update the wine server Z-order */ From d4393bafd2b92fbfd791af1ec5bd958811e3c25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 12 Jun 2023 23:32:59 +0200 Subject: [PATCH 522/758] win32u: Move fullscreen window cursor clipping from winex11. (cherry picked from commit af902c188ebaf7d6dda3b628409d8c390ab410d5) --- dlls/win32u/input.c | 67 ++++++++++++++++++++++++--- dlls/win32u/message.c | 8 +++- dlls/win32u/ntgdi_private.h | 9 ---- dlls/win32u/sysparams.c | 4 ++ dlls/win32u/win32u_private.h | 16 ++++++- dlls/win32u/winstation.c | 13 ++++++ dlls/winex11.drv/desktop.c | 3 -- dlls/winex11.drv/event.c | 7 +-- dlls/winex11.drv/mouse.c | 89 +----------------------------------- dlls/winex11.drv/window.c | 2 - dlls/winex11.drv/x11drv.h | 4 -- 11 files changed, 102 insertions(+), 120 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 9530cba031b..61adcd1bff8 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -407,6 +407,7 @@ static const KBDTABLES kbdus_tables = static LONG clipping_cursor; /* clipping thread counter */ BOOL grab_pointer = TRUE; +BOOL grab_fullscreen = FALSE; static void kbd_tables_init_vsc2vk( const KBDTABLES *tables, BYTE vsc2vk[0x300] ) { @@ -1855,7 +1856,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) if (previous == hwnd) { if (prev) *prev = hwnd; - return TRUE; + goto done; } if (prev) *prev = previous; @@ -1952,6 +1953,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) done: win_set_flags( hwnd, 0, WIN_IS_ACTIVATING ); + if (ret && hwnd) clip_fullscreen_window( hwnd, FALSE ); return ret; } @@ -2478,6 +2480,49 @@ BOOL WINAPI NtUserIsMouseInPointerEnabled(void) return FALSE; } +/*********************************************************************** + * clip_fullscreen_window + * + * Turn on clipping if the active window is fullscreen. + */ +BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + MONITORINFO monitor_info = {.cbSize = sizeof(MONITORINFO)}; + RECT rect, virtual_rect = NtUserGetVirtualScreenRect(); + HMONITOR monitor; + DWORD style; + + if (hwnd == NtUserGetDesktopWindow()) return FALSE; + if (hwnd != NtUserGetForegroundWindow()) return FALSE; + + style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + if (!(style & WS_VISIBLE)) return FALSE; + if ((style & (WS_POPUP | WS_CHILD)) == WS_CHILD) return FALSE; + /* maximized windows don't count as full screen */ + if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) return FALSE; + + if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; + if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; + if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; + if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ + + if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; + if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; + if (!grab_fullscreen) + { + RECT virtual_rect = NtUserGetVirtualScreenRect(); + if (!EqualRect( &monitor_info.rcMonitor, &virtual_rect )) return FALSE; + if (is_virtual_desktop()) return FALSE; + } + + /* shrink the clipping rect to make sure it is not ignored for being fullscreen */ + if (EqualRect( &monitor_info.rcMonitor, &virtual_rect )) InflateRect( &monitor_info.rcMonitor, -1, -1 ); + + TRACE( "win %p clipping fullscreen\n", hwnd ); + return NtUserClipCursor( &monitor_info.rcMonitor ); +} + /********************************************************************** * NtUserIsTouchWindow (win32u.@) */ @@ -2540,14 +2585,15 @@ BOOL get_clip_cursor( RECT *rect ) return TRUE; } -BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) +BOOL process_wine_clipcursor( HWND hwnd, BOOL empty, BOOL reset ) { struct user_thread_info *thread_info = get_user_thread_info(); - RECT rect; + RECT rect, virtual_rect = NtUserGetVirtualScreenRect(); + BOOL was_clipping; - TRACE( "empty %u, reset %u\n", empty, reset ); + TRACE( "hwnd %p, empty %u, reset %u\n", hwnd, empty, reset ); - if (thread_info->clipping_cursor) InterlockedDecrement( &clipping_cursor ); + if ((was_clipping = thread_info->clipping_cursor)) InterlockedDecrement( &clipping_cursor ); thread_info->clipping_cursor = FALSE; if (reset) @@ -2557,9 +2603,18 @@ BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) } if (!grab_pointer) return TRUE; - if (empty) return user_driver->pClipCursor( NULL, reset ); + /* we are clipping if the clip rectangle is smaller than the screen */ get_clip_cursor( &rect ); + intersect_rect( &rect, &rect, &virtual_rect ); + if (EqualRect( &rect, &virtual_rect )) empty = TRUE; + if (empty) + { + /* if currently clipping, check if we should switch to fullscreen clipping */ + if (was_clipping && clip_fullscreen_window( hwnd, TRUE )) return TRUE; + return user_driver->pClipCursor( NULL, FALSE ); + } + if (!user_driver->pClipCursor( &rect, FALSE )) return FALSE; InterlockedIncrement( &clipping_cursor ); thread_info->clipping_cursor = TRUE; diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 8c0ba27efd5..9abdcf890a3 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1287,7 +1287,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam ); } case WM_WINE_CLIPCURSOR: - return process_wine_clipcursor( wparam, lparam ); + if (wparam && lparam) return clip_fullscreen_window( hwnd, FALSE ); + return process_wine_clipcursor( hwnd, wparam, lparam ); case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; @@ -1868,7 +1869,7 @@ static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardwar else if (is_mouse_message( msg->message )) ret = process_mouse_message( msg, hw_id, msg_data->info, hwnd_filter, first, last, remove ); else if (msg->message == WM_WINE_CLIPCURSOR) - process_wine_clipcursor( msg->wParam, msg->lParam ); + process_wine_clipcursor( msg->hwnd, msg->wParam, msg->lParam ); else ERR( "unknown message type %x\n", msg->message ); SetThreadDpiAwarenessContext( context ); @@ -2759,6 +2760,9 @@ NTSTATUS send_hardware_message( HWND hwnd, const INPUT *input, const RAWINPUT *r info.timeout = 0; info.params = NULL; + if (input->type == INPUT_MOUSE && (input->mi.dwFlags & (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_RIGHTDOWN))) + clip_fullscreen_window( hwnd, FALSE ); + if (input->type == INPUT_HARDWARE && rawinput->header.dwType == RIM_TYPEHID) { if (input->hi.uMsg == WM_INPUT_DEVICE_CHANGE) diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index 93be59c9d7e..3262f5c2e2a 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -528,15 +528,6 @@ static inline DC *get_physdev_dc( PHYSDEV dev ) return get_nulldrv_dc( dev ); } -static inline BOOL intersect_rect( RECT *dst, const RECT *src1, const RECT *src2 ) -{ - dst->left = max( src1->left, src2->left ); - dst->top = max( src1->top, src2->top ); - dst->right = min( src1->right, src2->right ); - dst->bottom = min( src1->bottom, src2->bottom ); - return !IsRectEmpty( dst ); -} - static inline void order_rect( RECT *rect ) { if (rect->left > rect->right) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 0f730dcac52..b1e058a2db3 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2705,6 +2705,8 @@ static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmod send_message_timeout( HWND_BROADCAST, WM_DISPLAYCHANGE, current_mode.dmBitsPerPel, MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), SMTO_ABORTIFHUNG, 2000, FALSE ); + /* post clip_fullscreen_window request to the foreground window */ + NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, TRUE, TRUE ); } return ret; @@ -4243,6 +4245,8 @@ void sysparams_init(void) if (!get_config_key( hkey, appkey, "GrabPointer", buffer, sizeof(buffer) )) grab_pointer = IS_OPTION_TRUE( buffer[0] ); + if (!get_config_key( hkey, appkey, "GrabFullscreen", buffer, sizeof(buffer) )) + grab_fullscreen = IS_OPTION_TRUE( buffer[0] ); #undef IS_OPTION_TRUE } diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 7faa33f2aad..c78d039d91f 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -264,6 +264,7 @@ extern void unregister_imm_window( HWND hwnd ) DECLSPEC_HIDDEN; /* input.c */ extern BOOL grab_pointer DECLSPEC_HIDDEN; +extern BOOL grab_fullscreen DECLSPEC_HIDDEN; extern BOOL destroy_caret(void) DECLSPEC_HIDDEN; extern BOOL enable_mouse_in_pointer DECLSPEC_HIDDEN; extern HWND get_active_window(void) DECLSPEC_HIDDEN; @@ -281,7 +282,8 @@ extern void toggle_caret( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL unregister_touch_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN; -extern BOOL process_wine_clipcursor( BOOL empty, BOOL reset ) DECLSPEC_HIDDEN; +extern BOOL process_wine_clipcursor( HWND hwnd, BOOL empty, BOOL reset ) DECLSPEC_HIDDEN; +extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; /* menu.c */ extern HMENU create_menu( BOOL is_popup ) DECLSPEC_HIDDEN; @@ -362,6 +364,9 @@ extern void user_lock(void) DECLSPEC_HIDDEN; extern void user_unlock(void) DECLSPEC_HIDDEN; extern void user_check_not_lock(void) DECLSPEC_HIDDEN; +/* winstation.c */ +extern BOOL is_virtual_desktop(void) DECLSPEC_HIDDEN; + /* window.c */ struct tagWND; extern HDWP begin_defer_window_pos( INT count ) DECLSPEC_HIDDEN; @@ -528,4 +533,13 @@ static inline const char *debugstr_color( COLORREF color ) return wine_dbg_sprintf( "RGB(%02x,%02x,%02x)", GetRValue(color), GetGValue(color), GetBValue(color) ); } +static inline BOOL intersect_rect( RECT *dst, const RECT *src1, const RECT *src2 ) +{ + dst->left = max( src1->left, src2->left ); + dst->top = max( src1->top, src2->top ); + dst->right = min( src1->right, src2->right ); + dst->bottom = min( src1->bottom, src2->bottom ); + return !IsRectEmpty( dst ); +} + #endif /* __WINE_WIN32U_PRIVATE */ diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 8ab6122709e..f0190d2335e 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -40,6 +40,19 @@ WINE_DECLARE_DEBUG_CHANNEL(win); #define DESKTOP_ALL_ACCESS 0x01ff +BOOL is_virtual_desktop(void) +{ + HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); + USEROBJECTFLAGS flags = {0}; + NTSTATUS status; + DWORD len; + + status = NtUserGetObjectInformation( desktop, UOI_FLAGS, &flags, sizeof(flags), &len ); + if (status) return FALSE; + + return !!(flags.dwFlags & DF_WINE_CREATE_DESKTOP); +} + /*********************************************************************** * NtUserCreateWindowStation (win32u.@) */ diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index f0c038d8981..9bfdf24dc01 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -453,8 +453,5 @@ void X11DRV_resize_desktop(void) send_message_timeout( HWND_BROADCAST, WM_X11DRV_DESKTOP_RESIZED, old_virtual_rect.left, old_virtual_rect.top, SMTO_ABORTIFHUNG, 2000, FALSE ); - /* forward clip_fullscreen_window request to the foreground window */ - send_notify_message( NtUserGetForegroundWindow(), WM_X11DRV_CLIP_CURSOR_REQUEST, TRUE, TRUE ); - old_virtual_rect = virtual_rect; } diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index d7e68827cbc..45cf20aa0b2 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -891,11 +891,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) } xim_set_focus( hwnd, TRUE ); - if (use_take_focus) - { - clip_fullscreen_window( hwnd, FALSE ); - return TRUE; - } + + if (use_take_focus) return TRUE; if (!can_activate_window(hwnd)) { diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 781bd4fe575..1993e973d24 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -539,7 +539,6 @@ static void ungrab_clipping_window(void) clipping_cursor = FALSE; NtUserDestroyWindow( data->clip_hwnd ); data->clip_hwnd = 0; - data->clip_reset = NtGetTickCount(); X11DRV_XInput2_Enable( data->display, None, 0 ); } @@ -557,50 +556,6 @@ void retry_grab_clipping_window(void) NtUserClipCursor( &last_clip_rect ); } -/*********************************************************************** - * clip_fullscreen_window - * - * Turn on clipping if the active window is fullscreen. - */ -BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) -{ - struct x11drv_win_data *data; - struct x11drv_thread_data *thread_data; - MONITORINFO monitor_info; - HMONITOR monitor; - DWORD style; - BOOL fullscreen; - - if (hwnd == NtUserGetDesktopWindow()) return FALSE; - if (hwnd != NtUserGetForegroundWindow()) return FALSE; - - style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); - if (!(style & WS_VISIBLE)) return FALSE; - if ((style & (WS_POPUP | WS_CHILD)) == WS_CHILD) return FALSE; - /* maximized windows don't count as full screen */ - if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) return FALSE; - if (!(data = get_win_data( hwnd ))) return FALSE; - fullscreen = NtUserIsWindowRectFullScreen( &data->whole_rect ); - release_win_data( data ); - if (!fullscreen) return FALSE; - if (!(thread_data = x11drv_thread_data())) return FALSE; - if (NtGetTickCount() - thread_data->clip_reset < 1000) return FALSE; - if (!reset && clipping_cursor && thread_data->clip_hwnd) return FALSE; /* already clipping */ - - monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ); - if (!monitor) return FALSE; - monitor_info.cbSize = sizeof(monitor_info); - if (!NtUserGetMonitorInfo( monitor, &monitor_info )) return FALSE; - if (!grab_fullscreen) - { - RECT virtual_rect = NtUserGetVirtualScreenRect(); - if (!EqualRect( &monitor_info.rcMonitor, &virtual_rect )) return FALSE; - if (is_virtual_desktop()) return FALSE; - } - TRACE( "win %p clipping fullscreen\n", hwnd ); - return grab_clipping_window( &monitor_info.rcMonitor ); -} - /*********************************************************************** * is_old_motion_event @@ -710,9 +665,6 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU last_cursor_change = input->u.mi.time; } - if (input->u.mi.dwFlags & (MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_RIGHTDOWN)) - clip_fullscreen_window( hwnd, FALSE ); - /* update the wine server Z-order */ if (hwnd != x11drv_thread_data()->grab_hwnd && @@ -1608,50 +1560,11 @@ BOOL X11DRV_GetCursorPos(LPPOINT pos) */ BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) { - TRACE( "clip %p, reset %u\n", clip, reset ); - - if (!reset) - { - RECT virtual_rect = NtUserGetVirtualScreenRect(); - - if (!clip) clip = &virtual_rect; - - /* we are clipping if the clip rectangle is smaller than the screen */ - if (clip->left > virtual_rect.left || clip->right < virtual_rect.right || - clip->top > virtual_rect.top || clip->bottom < virtual_rect.bottom) - { - if (grab_clipping_window( clip )) return TRUE; - } - else /* if currently clipping, check if we should switch to fullscreen clipping */ - { - struct x11drv_thread_data *data = x11drv_thread_data(); - if (data && data->clip_hwnd) - { - if (EqualRect( clip, &clip_rect )) return TRUE; - if (clip_fullscreen_window( NtUserGetForegroundWindow(), TRUE )) return TRUE; - } - } - } + if (!reset && clip && grab_clipping_window( clip )) return TRUE; ungrab_clipping_window(); return TRUE; } -/*********************************************************************** - * clip_cursor_request - * - * Function called upon receiving a WM_X11DRV_CLIP_CURSOR_REQUEST. - */ -LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) -{ - if (hwnd == NtUserGetDesktopWindow()) - WARN( "ignoring clip cursor request on desktop window.\n" ); - else if (hwnd != NtUserGetForegroundWindow()) - WARN( "ignoring clip cursor request on non-foreground window.\n" ); - else if (fullscreen) - clip_fullscreen_window( hwnd, reset ); - return 0; -} - /*********************************************************************** * move_resize_window */ diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 53b22db4695..ddf396d4556 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3742,8 +3742,6 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) } return 0; } - case WM_X11DRV_CLIP_CURSOR_REQUEST: - return clip_cursor_request( hwnd, (BOOL)wp, (BOOL)lp ); case WM_X11DRV_DELETE_TAB: taskbar_delete_tab( hwnd ); return 0; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index ade0deb45da..663f510965d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -393,7 +393,6 @@ struct x11drv_thread_data unsigned long warp_serial; /* serial number of last pointer warp request */ Window clip_window; /* window used for cursor clipping */ HWND clip_hwnd; /* message window stored in desktop while clipping is active */ - DWORD clip_reset; /* time when clipping was last reset */ #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H XIValuatorClassInfo x_valuator; XIValuatorClassInfo y_valuator; @@ -608,7 +607,6 @@ enum x11drv_window_messages WM_X11DRV_SET_WIN_REGION, WM_X11DRV_DESKTOP_RESIZED, WM_X11DRV_SET_CURSOR, - WM_X11DRV_CLIP_CURSOR_REQUEST, WM_X11DRV_DELETE_TAB, WM_X11DRV_ADD_TAB }; @@ -736,9 +734,7 @@ extern XContext cursor_context DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; -extern LRESULT clip_cursor_request( HWND hwnd, BOOL fullscreen, BOOL reset ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; -extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; extern void X11DRV_InitKeyboard( Display *display ) DECLSPEC_HIDDEN; extern void X11DRV_InitMouse( Display *display ) DECLSPEC_HIDDEN; From 0e5fe9a2313020ab65000666bb4a578e09150f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 13 Jun 2023 23:05:01 +0200 Subject: [PATCH 523/758] server: Update desktop cursor window when cursor pos changes. (cherry picked from commit 10f5ff7f06595bf9d1f487f91dbbd869e5105d26) --- server/queue.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/server/queue.c b/server/queue.c index abbe99e0042..15c01e2e77e 100644 --- a/server/queue.c +++ b/server/queue.c @@ -489,8 +489,16 @@ static struct message *alloc_hardware_message( lparam_t info, struct hw_msg_sour return msg; } -static int update_desktop_cursor_pos( struct desktop *desktop, int x, int y ) +static int update_desktop_cursor_window( struct desktop *desktop, user_handle_t win ) { + int updated = win != desktop->cursor_win; + desktop->cursor_win = win; + return updated; +} + +static int update_desktop_cursor_pos( struct desktop *desktop, user_handle_t win, int x, int y ) +{ + struct thread_input *input; int updated; unsigned int time = get_tick_count(); @@ -504,6 +512,11 @@ static int update_desktop_cursor_pos( struct desktop *desktop, int x, int y ) desktop->shared->cursor.last_change = time; SHARED_WRITE_END( &desktop->shared->seq ); + if (!win && (input = desktop->foreground_input)) win = input->shared->capture; + if (!win || !is_window_visible( win ) || is_window_transparent( win )) + win = shallow_window_from_point( desktop, x, y ); + if (update_desktop_cursor_window( desktop, win )) updated = 1; + return updated; } @@ -516,7 +529,7 @@ static void set_cursor_pos( struct desktop *desktop, int x, int y ) if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) { - update_desktop_cursor_pos( desktop, x, y ); + update_desktop_cursor_pos( desktop, 0, x, y ); return; } @@ -1691,11 +1704,8 @@ static void update_desktop_key_state( struct desktop *desktop, unsigned int msg, } /* update the desktop key state according to a mouse message flags */ -static void update_desktop_mouse_state( struct desktop *desktop, unsigned int flags, - int x, int y, lparam_t wparam ) +static void update_desktop_mouse_state( struct desktop *desktop, unsigned int flags, lparam_t wparam ) { - if (flags & MOUSEEVENTF_MOVE) - update_desktop_cursor_pos( desktop, x, y ); if (flags & MOUSEEVENTF_LEFTDOWN) update_desktop_key_state( desktop, WM_LBUTTONDOWN, wparam ); if (flags & MOUSEEVENTF_LEFTUP) @@ -1867,12 +1877,12 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg if (IS_POINTER_PRIMARY_WPARAM( msg_data->rawinput.mouse.data )) { prepend_cursor_history( msg->x, msg->y, msg->time, msg_data->info ); - if (update_desktop_cursor_pos( desktop, msg->x, msg->y )) always_queue = 1; + if (update_desktop_cursor_pos( desktop, msg->win, msg->x, msg->y )) always_queue = 1; } break; case QS_MOUSEMOVE: prepend_cursor_history( msg->x, msg->y, msg->time, msg_data->info ); - if (update_desktop_cursor_pos( desktop, msg->x, msg->y )) always_queue = 1; + if (update_desktop_cursor_pos( desktop, msg->win, msg->x, msg->y )) always_queue = 1; /* fallthrough */ case QS_MOUSEBUTTON: if (desktop->shared->keystate[VK_LBUTTON] & 0x80) msg->wparam |= MK_LBUTTON; @@ -1903,9 +1913,6 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg } input = thread->queue->input; - if (win != desktop->cursor_win) always_queue = 1; - desktop->cursor_win = win; - if (!always_queue || merge_message( input, msg )) free_message( msg ); else { @@ -2082,7 +2089,7 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons if ((input->mouse.info & 0xffffff00) == 0xff515700) source.origin = IMDT_TOUCH; - update_desktop_cursor_pos( desktop, desktop->shared->cursor.x, desktop->shared->cursor.y ); /* Update last change time */ + update_desktop_cursor_pos( desktop, desktop->cursor_win, desktop->shared->cursor.x, desktop->shared->cursor.y ); /* Update last change time */ flags = input->mouse.flags; time = input->mouse.time; if (!time) time = desktop->shared->cursor.last_change; @@ -2133,7 +2140,8 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) { - update_desktop_mouse_state( desktop, flags, x, y, input->mouse.data << 16 ); + if (flags & MOUSEEVENTF_MOVE) update_desktop_cursor_pos( desktop, win, x, y ); + update_desktop_mouse_state( desktop, flags, input->mouse.data << 16 ); return 0; } From 72978f1929801719f6fa56ed1d52db41dac06258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 13 Jun 2023 23:06:52 +0200 Subject: [PATCH 524/758] server: Keep track of the current desktop cursor handle. (cherry picked from commit b04ef1993054a5e8dc3413beadf10150457a7b39) --- server/queue.c | 11 +++++++++++ server/user.h | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/server/queue.c b/server/queue.c index 15c01e2e77e..c9dd06be23a 100644 --- a/server/queue.c +++ b/server/queue.c @@ -520,6 +520,11 @@ static int update_desktop_cursor_pos( struct desktop *desktop, user_handle_t win return updated; } +static void update_desktop_cursor_handle( struct desktop *desktop, user_handle_t handle ) +{ + desktop->cursor_handle = handle; +} + /* set the cursor position and queue the corresponding mouse message */ static void set_cursor_pos( struct desktop *desktop, int x, int y ) { @@ -3776,6 +3781,12 @@ DECL_HANDLER(set_cursor) if (req->flags & SET_CURSOR_CLIP) set_clip_rectangle( desktop, &req->clip, 0 ); if (req->flags & SET_CURSOR_NOCLIP) set_clip_rectangle( desktop, NULL, 0 ); + if (req->flags & (SET_CURSOR_HANDLE | SET_CURSOR_COUNT)) + { + if (input->shared->cursor_count < 0) update_desktop_cursor_handle( desktop, 0 ); + else update_desktop_cursor_handle( desktop, input->shared->cursor ); + } + reply->new_x = desktop->shared->cursor.x; reply->new_y = desktop->shared->cursor.y; reply->new_clip = desktop->shared->cursor.clip; diff --git a/server/user.h b/server/user.h index 4f6dc4755d5..347e84e10bc 100644 --- a/server/user.h +++ b/server/user.h @@ -67,7 +67,8 @@ struct desktop struct list touches; /* list of active touches */ struct thread_input *foreground_input; /* thread input of foreground thread */ unsigned int users; /* processes and threads using this desktop */ - user_handle_t cursor_win; /* window that contains the cursor */ + user_handle_t cursor_win; /* window that contains the cursor */ + user_handle_t cursor_handle; /* last set cursor handle */ struct object *shared_mapping; /* desktop shared memory mapping */ volatile struct desktop_shared_memory *shared; /* desktop shared memory ptr */ unsigned int last_press_alt:1; /* last key press was Alt (used to determine msg on Alt release) */ From b2cbc89f56f0f807914aeb19f479a139b0f97e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 10:38:46 +0200 Subject: [PATCH 525/758] server: Introduce and send new WM_WINE_SETCURSOR hardware message. (cherry picked from commit 25906eedd8679fdb474976563f4a05a92e11bbd6) --- dlls/win32u/cursoricon.c | 6 ++++ dlls/win32u/message.c | 5 +++ dlls/win32u/win32u_private.h | 1 + include/ntuser.h | 1 + server/queue.c | 69 ++++++++++++++++++++++++------------ 5 files changed, 60 insertions(+), 22 deletions(-) diff --git a/dlls/win32u/cursoricon.c b/dlls/win32u/cursoricon.c index bfcb805901c..b09ee74f793 100644 --- a/dlls/win32u/cursoricon.c +++ b/dlls/win32u/cursoricon.c @@ -74,6 +74,12 @@ static struct cursoricon_object *get_icon_ptr( HICON handle ) return obj; } +BOOL process_wine_setcursor( HWND hwnd, HWND window, HCURSOR handle ) +{ + TRACE( "hwnd %p, window %p, hcursor %p\n", hwnd, window, handle ); + return TRUE; +} + /*********************************************************************** * NtUserShowCursor (win32u.@) */ diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 9abdcf890a3..eb700321708 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1289,6 +1289,9 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR case WM_WINE_CLIPCURSOR: if (wparam && lparam) return clip_fullscreen_window( hwnd, FALSE ); return process_wine_clipcursor( hwnd, wparam, lparam ); + case WM_WINE_SETCURSOR: + FIXME( "Unexpected non-hardware WM_WINE_SETCURSOR message\n" ); + return FALSE; case WM_WINE_UPDATEWINDOWSTATE: update_window_state( hwnd ); return 0; @@ -1870,6 +1873,8 @@ static BOOL process_hardware_message( MSG *msg, UINT hw_id, const struct hardwar ret = process_mouse_message( msg, hw_id, msg_data->info, hwnd_filter, first, last, remove ); else if (msg->message == WM_WINE_CLIPCURSOR) process_wine_clipcursor( msg->hwnd, msg->wParam, msg->lParam ); + else if (msg->message == WM_WINE_SETCURSOR) + process_wine_setcursor( msg->hwnd, (HWND)msg->wParam, (HCURSOR)msg->lParam ); else ERR( "unknown message type %x\n", msg->message ); SetThreadDpiAwarenessContext( context ); diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index c78d039d91f..784bb9778ae 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -215,6 +215,7 @@ extern UINT enum_clipboard_formats( UINT format ) DECLSPEC_HIDDEN; extern void release_clipboard_owner( HWND hwnd ) DECLSPEC_HIDDEN; /* cursoricon.c */ +extern BOOL process_wine_setcursor( HWND hwnd, HWND window, HCURSOR handle ) DECLSPEC_HIDDEN; extern HICON alloc_cursoricon_handle( BOOL is_icon ) DECLSPEC_HIDDEN; extern ULONG_PTR get_icon_param( HICON handle ) DECLSPEC_HIDDEN; extern ULONG_PTR set_icon_param( HICON handle, ULONG_PTR param ) DECLSPEC_HIDDEN; diff --git a/include/ntuser.h b/include/ntuser.h index 1e2b47e627d..5ba3ae1dfd2 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -483,6 +483,7 @@ enum wine_internal_message WM_WINE_KEYBOARD_LL_HOOK, WM_WINE_MOUSE_LL_HOOK, WM_WINE_CLIPCURSOR, + WM_WINE_SETCURSOR, WM_WINE_UPDATEWINDOWSTATE, WM_WINE_FIRST_DRIVER_MSG = 0x80001000, /* range of messages reserved for the USER driver */ WM_WINE_LAST_DRIVER_MSG = 0x80001fff diff --git a/server/queue.c b/server/queue.c index c9dd06be23a..0a7b5fa70aa 100644 --- a/server/queue.c +++ b/server/queue.c @@ -489,10 +489,42 @@ static struct message *alloc_hardware_message( lparam_t info, struct hw_msg_sour return msg; } +static int is_cursor_clipped( struct desktop *desktop ) +{ + rectangle_t top_rect, clip_rect = desktop->shared->cursor.clip; + get_top_window_rectangle( desktop, &top_rect ); + return !is_rect_equal( &clip_rect, &top_rect ); +} + +static void queue_cursor_message( struct desktop *desktop, user_handle_t win, unsigned int message, + lparam_t wparam, lparam_t lparam ) +{ + static const struct hw_msg_source source = { IMDT_UNAVAILABLE, IMO_SYSTEM }; + struct thread_input *input; + struct message *msg; + + if (!(msg = alloc_hardware_message( 0, source, get_tick_count(), 0 ))) return; + + msg->msg = message; + msg->wparam = wparam; + msg->lparam = lparam; + msg->x = desktop->shared->cursor.x; + msg->y = desktop->shared->cursor.y; + if (!(msg->win = win) && (input = desktop->foreground_input)) msg->win = input->shared->active; + queue_hardware_message( desktop, msg, 1 ); +} + static int update_desktop_cursor_window( struct desktop *desktop, user_handle_t win ) { int updated = win != desktop->cursor_win; + user_handle_t handle = desktop->cursor_handle; desktop->cursor_win = win; + if (updated) + { + /* when clipping send the message to the foreground window as well, as some driver have an artificial overlay window */ + if (is_cursor_clipped( desktop )) queue_cursor_message( desktop, 0, WM_WINE_SETCURSOR, win, handle ); + queue_cursor_message( desktop, win, WM_WINE_SETCURSOR, win, handle ); + } return updated; } @@ -522,7 +554,15 @@ static int update_desktop_cursor_pos( struct desktop *desktop, user_handle_t win static void update_desktop_cursor_handle( struct desktop *desktop, user_handle_t handle ) { + int updated = desktop->cursor_handle != handle; + user_handle_t win = desktop->cursor_win; desktop->cursor_handle = handle; + if (updated) + { + /* when clipping send the message to the foreground window as well, as some driver have an artificial overlay window */ + if (is_cursor_clipped( desktop )) queue_cursor_message( desktop, 0, WM_WINE_SETCURSOR, win, handle ); + queue_cursor_message( desktop, win, WM_WINE_SETCURSOR, win, handle ); + } } /* set the cursor position and queue the corresponding mouse message */ @@ -556,23 +596,6 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig *time = get_tick_count(); } -static void queue_clip_cursor_msg( struct desktop *desktop, lparam_t wparam, lparam_t lparam ) -{ - static const struct hw_msg_source source = { IMDT_UNAVAILABLE, IMO_SYSTEM }; - struct thread_input *input; - struct message *msg; - - if (!(msg = alloc_hardware_message( 0, source, get_tick_count(), 0 ))) return; - - msg->msg = WM_WINE_CLIPCURSOR; - msg->wparam = wparam; - msg->lparam = lparam; - msg->x = desktop->shared->cursor.x; - msg->y = desktop->shared->cursor.y; - if ((input = desktop->foreground_input)) msg->win = input->shared->active; - queue_hardware_message( desktop, msg, 1 ); -} - /* set the cursor clip rectangle */ void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int reset ) { @@ -604,7 +627,7 @@ void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int r if (reset) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, TRUE, FALSE ); /* notify foreground thread, of reset, or to apply new cursor clipping rect */ - queue_clip_cursor_msg( desktop, rect == NULL, reset ); + queue_cursor_message( desktop, 0, WM_WINE_CLIPCURSOR, rect == NULL, reset ); } /* change the foreground input and reset the cursor clip rect */ @@ -712,6 +735,7 @@ static inline int get_hardware_msg_bit( unsigned int message ) if (message == WM_POINTERDOWN || message == WM_POINTERUP || message == WM_POINTERUPDATE) return QS_POINTER; if (message == WM_WINE_CLIPCURSOR) return QS_RAWINPUT; + if (message == WM_WINE_SETCURSOR) return QS_RAWINPUT; return QS_MOUSEBUTTON; } @@ -794,13 +818,13 @@ static int merge_mousemove( struct thread_input *input, const struct message *ms return 1; } -/* try to merge a WM_WINE_CLIPCURSOR message with the last in the list; return 1 if successful */ -static int merge_wine_clipcursor( struct thread_input *input, const struct message *msg ) +/* try to merge a unique message with the last in the list; return 1 if successful */ +static int merge_unique_message( struct thread_input *input, unsigned int message, const struct message *msg ) { struct message *prev; LIST_FOR_EACH_ENTRY_REV( prev, &input->msg_list, struct message, entry ) - if (prev->msg == WM_WINE_CLIPCURSOR) break; + if (prev->msg == message) break; if (&prev->entry == &input->msg_list) return 0; if (prev->result) return 0; @@ -824,7 +848,8 @@ static int merge_message( struct thread_input *input, const struct message *msg { if (msg->msg == WM_POINTERUPDATE) return merge_pointer_update_message( input, msg ); if (msg->msg == WM_MOUSEMOVE) return merge_mousemove( input, msg ); - if (msg->msg == WM_WINE_CLIPCURSOR) return merge_wine_clipcursor( input, msg ); + if (msg->msg == WM_WINE_CLIPCURSOR) return merge_unique_message( input, WM_WINE_CLIPCURSOR, msg ); + if (msg->msg == WM_WINE_SETCURSOR) return merge_unique_message( input, WM_WINE_SETCURSOR, msg ); return 0; } From c28998b4d8ec0353ce57fb60a1b448144bcfda57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Jan 2023 11:09:30 +0100 Subject: [PATCH 526/758] win32u: Add a hwnd parameter to SetCursor driver entry points. (cherry picked from commit 3afff1a690b17bd3cf3685c5538f542a4afc0d48) --- dlls/win32u/cursoricon.c | 6 +++--- dlls/win32u/driver.c | 6 +++--- dlls/wineandroid.drv/android.h | 3 +-- dlls/wineandroid.drv/window.c | 2 +- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/mouse.c | 4 ++-- dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/x11drv.h | 2 +- include/wine/gdi_driver.h | 2 +- 9 files changed, 14 insertions(+), 15 deletions(-) diff --git a/dlls/win32u/cursoricon.c b/dlls/win32u/cursoricon.c index b09ee74f793..6045852459b 100644 --- a/dlls/win32u/cursoricon.c +++ b/dlls/win32u/cursoricon.c @@ -101,8 +101,8 @@ INT WINAPI NtUserShowCursor( BOOL show ) TRACE("%d, count=%d\n", show, count ); - if (show && !count) user_driver->pSetCursor( cursor ); - else if (!show && count == -1) user_driver->pSetCursor( 0 ); + if (show && !count) user_driver->pSetCursor( 0, cursor ); + else if (!show && count == -1) user_driver->pSetCursor( 0, 0 ); return count; } @@ -132,7 +132,7 @@ HCURSOR WINAPI NtUserSetCursor( HCURSOR cursor ) SERVER_END_REQ; if (!ret) return 0; - user_driver->pSetCursor( show_count >= 0 ? cursor : 0 ); + user_driver->pSetCursor( 0, show_count >= 0 ? cursor : 0 ); if (!(obj = get_icon_ptr( old_cursor ))) return 0; release_user_handle_ptr( obj ); diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 3a96b45d03f..2a1da075dd4 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -739,7 +739,7 @@ static void nulldrv_DestroyCursorIcon( HCURSOR cursor ) { } -static void nulldrv_SetCursor( HCURSOR cursor ) +static void nulldrv_SetCursor( HWND hwnd, HCURSOR cursor ) { } @@ -1119,9 +1119,9 @@ static INT loaderdrv_GetDisplayDepth( LPCWSTR name, BOOL is_primary ) return load_driver()->pGetDisplayDepth( name, is_primary ); } -static void loaderdrv_SetCursor( HCURSOR cursor ) +static void loaderdrv_SetCursor( HWND hwnd, HCURSOR cursor ) { - load_driver()->pSetCursor( cursor ); + load_driver()->pSetCursor( hwnd, cursor ); } static BOOL loaderdrv_GetCursorPos( POINT *pt ) diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h index d12cfa28f04..2eb6a288a72 100644 --- a/dlls/wineandroid.drv/android.h +++ b/dlls/wineandroid.drv/android.h @@ -86,13 +86,12 @@ extern pthread_mutex_t win_data_mutex DECLSPEC_HIDDEN; extern INT ANDROID_GetKeyNameText( LONG lparam, LPWSTR buffer, INT size ) DECLSPEC_HIDDEN; extern UINT ANDROID_MapVirtualKeyEx( UINT code, UINT maptype, HKL hkl ) DECLSPEC_HIDDEN; extern SHORT ANDROID_VkKeyScanEx( WCHAR ch, HKL hkl ) DECLSPEC_HIDDEN; -extern void ANDROID_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; +extern void ANDROID_SetCursor( HWND hwnd, HCURSOR handle ) DECLSPEC_HIDDEN; extern BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) DECLSPEC_HIDDEN; extern BOOL ANDROID_CreateWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern void ANDROID_DestroyWindow( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL ANDROID_ProcessEvents( DWORD mask ) DECLSPEC_HIDDEN; extern LRESULT ANDROID_DesktopWindowProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) DECLSPEC_HIDDEN; -extern void ANDROID_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; extern void ANDROID_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags ) DECLSPEC_HIDDEN; extern void ANDROID_SetParent( HWND hwnd, HWND parent, HWND old_parent ) DECLSPEC_HIDDEN; diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index 905f4672580..d3e1693c82e 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -1447,7 +1447,7 @@ static BOOL get_icon_info( HICON handle, ICONINFOEXW *ret ) /*********************************************************************** * ANDROID_SetCursor */ -void ANDROID_SetCursor( HCURSOR handle ) +void ANDROID_SetCursor( HWND hwnd, HCURSOR handle ) { static HCURSOR last_cursor; static DWORD last_cursor_change; diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 39cb31bfdca..a1919596b74 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -159,7 +159,7 @@ extern void macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags extern void macdrv_DestroyCursorIcon(HCURSOR cursor) DECLSPEC_HIDDEN; extern BOOL macdrv_GetCursorPos(LPPOINT pos) DECLSPEC_HIDDEN; extern void macdrv_SetCapture(HWND hwnd, UINT flags) DECLSPEC_HIDDEN; -extern void macdrv_SetCursor(HCURSOR cursor) DECLSPEC_HIDDEN; +extern void macdrv_SetCursor(HWND hwnd, HCURSOR cursor) DECLSPEC_HIDDEN; extern BOOL macdrv_SetCursorPos(INT x, INT y) DECLSPEC_HIDDEN; extern BOOL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey) DECLSPEC_HIDDEN; extern void macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c index 9dfd1801fc9..260831c44dc 100644 --- a/dlls/winemac.drv/mouse.c +++ b/dlls/winemac.drv/mouse.c @@ -745,12 +745,12 @@ static BOOL get_icon_info(HICON handle, ICONINFOEXW *ret) /*********************************************************************** * SetCursor (MACDRV.@) */ -void macdrv_SetCursor(HCURSOR cursor) +void macdrv_SetCursor(HWND hwnd, HCURSOR cursor) { CFStringRef cursor_name = NULL; CFArrayRef cursor_frames = NULL; - TRACE("%p\n", cursor); + TRACE("%p %p\n", hwnd, cursor); if (cursor) { diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 1993e973d24..2e9df02af86 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1482,7 +1482,7 @@ void X11DRV_DestroyCursorIcon( HCURSOR handle ) /*********************************************************************** * SetCursor (X11DRV.@) */ -void X11DRV_SetCursor( HCURSOR handle ) +void X11DRV_SetCursor( HWND hwnd, HCURSOR handle ) { if (InterlockedExchangePointer( (void **)&last_cursor, handle ) != handle || NtGetTickCount() - last_cursor_change > 100) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 663f510965d..393bf5df17f 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -215,7 +215,7 @@ extern UINT X11DRV_ImeToAsciiEx( UINT vkey, UINT vsc, const BYTE *state, extern SHORT X11DRV_VkKeyScanEx( WCHAR wChar, HKL hkl ) DECLSPEC_HIDDEN; extern void X11DRV_NotifyIMEStatus( HWND hwnd, UINT status ) DECLSPEC_HIDDEN; extern void X11DRV_DestroyCursorIcon( HCURSOR handle ) DECLSPEC_HIDDEN; -extern void X11DRV_SetCursor( HCURSOR handle ) DECLSPEC_HIDDEN; +extern void X11DRV_SetCursor( HWND hwnd, HCURSOR handle ) DECLSPEC_HIDDEN; extern BOOL X11DRV_SetCursorPos( INT x, INT y ) DECLSPEC_HIDDEN; extern BOOL X11DRV_GetCursorPos( LPPOINT pos ) DECLSPEC_HIDDEN; extern BOOL X11DRV_ClipCursor( const RECT *clip, BOOL reset ) DECLSPEC_HIDDEN; diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 510fdac6012..3caee28099e 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -293,7 +293,7 @@ struct user_driver_funcs void (*pNotifyIMEStatus)(HWND,UINT); /* cursor/icon functions */ void (*pDestroyCursorIcon)(HCURSOR); - void (*pSetCursor)(HCURSOR); + void (*pSetCursor)(HWND,HCURSOR); BOOL (*pGetCursorPos)(LPPOINT); BOOL (*pSetCursorPos)(INT,INT); BOOL (*pClipCursor)(const RECT*,BOOL); From f7e1e0a8cdd67f13020d1fc096bffd7a9e72db55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Jan 2023 11:52:44 +0100 Subject: [PATCH 527/758] win32u: Notify drivers of cursor changes on WM_WINE_SETCURSOR. (cherry picked from commit 4b968267c1169c5437e9fc5d8ec5d4fd24491436) --- dlls/win32u/cursoricon.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dlls/win32u/cursoricon.c b/dlls/win32u/cursoricon.c index 6045852459b..fa51788dce9 100644 --- a/dlls/win32u/cursoricon.c +++ b/dlls/win32u/cursoricon.c @@ -77,6 +77,7 @@ static struct cursoricon_object *get_icon_ptr( HICON handle ) BOOL process_wine_setcursor( HWND hwnd, HWND window, HCURSOR handle ) { TRACE( "hwnd %p, window %p, hcursor %p\n", hwnd, window, handle ); + user_driver->pSetCursor( window, handle ); return TRUE; } @@ -85,7 +86,6 @@ BOOL process_wine_setcursor( HWND hwnd, HWND window, HCURSOR handle ) */ INT WINAPI NtUserShowCursor( BOOL show ) { - HCURSOR cursor; int increment = show ? 1 : -1; int count; @@ -94,16 +94,11 @@ INT WINAPI NtUserShowCursor( BOOL show ) req->flags = SET_CURSOR_COUNT; req->show_count = increment; wine_server_call( req ); - cursor = wine_server_ptr_handle( reply->prev_handle ); count = reply->prev_count + increment; } SERVER_END_REQ; TRACE("%d, count=%d\n", show, count ); - - if (show && !count) user_driver->pSetCursor( 0, cursor ); - else if (!show && count == -1) user_driver->pSetCursor( 0, 0 ); - return count; } @@ -114,7 +109,6 @@ HCURSOR WINAPI NtUserSetCursor( HCURSOR cursor ) { struct cursoricon_object *obj; HCURSOR old_cursor; - int show_count; BOOL ret; TRACE( "%p\n", cursor ); @@ -124,16 +118,11 @@ HCURSOR WINAPI NtUserSetCursor( HCURSOR cursor ) req->flags = SET_CURSOR_HANDLE; req->handle = wine_server_user_handle( cursor ); if ((ret = !wine_server_call_err( req ))) - { old_cursor = wine_server_ptr_handle( reply->prev_handle ); - show_count = reply->prev_count; - } } SERVER_END_REQ; if (!ret) return 0; - user_driver->pSetCursor( 0, show_count >= 0 ? cursor : 0 ); - if (!(obj = get_icon_ptr( old_cursor ))) return 0; release_user_handle_ptr( obj ); return old_cursor; From 695f157e395c98a181bd831d153d3e971c9f28a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Jan 2023 12:30:04 +0100 Subject: [PATCH 528/758] wineandroid: Set the window cursor immediately in SetCursor. (cherry picked from commit b17b77c707cd3467fd2d00b6634d92868a1be57f) --- dlls/wineandroid.drv/window.c | 47 ++++++++++++++--------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index d3e1693c82e..74ab61b9a4f 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -1449,42 +1449,33 @@ static BOOL get_icon_info( HICON handle, ICONINFOEXW *ret ) */ void ANDROID_SetCursor( HWND hwnd, HCURSOR handle ) { - static HCURSOR last_cursor; - static DWORD last_cursor_change; - - if (InterlockedExchangePointer( (void **)&last_cursor, handle ) != handle || - NtGetTickCount() - last_cursor_change > 100) + if (handle) { - last_cursor_change = NtGetTickCount(); + unsigned int width = 0, height = 0, *bits = NULL; + ICONINFOEXW info; + int id; - if (handle) - { - unsigned int width = 0, height = 0, *bits = NULL; - ICONINFOEXW info; - int id; + if (!get_icon_info( handle, &info )) return; - if (!get_icon_info( handle, &info )) return; + if (!(id = get_cursor_system_id( &info ))) + { + HDC hdc = NtGdiCreateCompatibleDC( 0 ); + bits = get_bitmap_argb( hdc, info.hbmColor, info.hbmMask, &width, &height ); + NtGdiDeleteObjectApp( hdc ); - if (!(id = get_cursor_system_id( &info ))) + /* make sure hotspot is valid */ + if (info.xHotspot >= width || info.yHotspot >= height) { - HDC hdc = NtGdiCreateCompatibleDC( 0 ); - bits = get_bitmap_argb( hdc, info.hbmColor, info.hbmMask, &width, &height ); - NtGdiDeleteObjectApp( hdc ); - - /* make sure hotspot is valid */ - if (info.xHotspot >= width || info.yHotspot >= height) - { - info.xHotspot = width / 2; - info.yHotspot = height / 2; - } + info.xHotspot = width / 2; + info.yHotspot = height / 2; } - ioctl_set_cursor( id, width, height, info.xHotspot, info.yHotspot, bits ); - free( bits ); - NtGdiDeleteObjectApp( info.hbmColor ); - NtGdiDeleteObjectApp( info.hbmMask ); } - else ioctl_set_cursor( 0, 0, 0, 0, 0, NULL ); + ioctl_set_cursor( id, width, height, info.xHotspot, info.yHotspot, bits ); + free( bits ); + NtGdiDeleteObjectApp( info.hbmColor ); + NtGdiDeleteObjectApp( info.hbmMask ); } + else ioctl_set_cursor( 0, 0, 0, 0, 0, NULL ); } From c01eeb67245c0b53f5cb8dad04b4e56e5bcae9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 13 Jun 2023 23:09:55 +0200 Subject: [PATCH 529/758] winex11: Set the window cursor immediately in SetCursor. Instead of synchronizing the cursor on mouse changes. The SetCursor call should now only be made when cursor or window handle have changed. (cherry picked from commit 9f8d9eef216d4ede3b53d3caab222ad8f896f10b) --- dlls/winex11.drv/mouse.c | 61 ++++++++++++--------------------------- dlls/winex11.drv/window.c | 23 --------------- dlls/winex11.drv/x11drv.h | 2 -- 3 files changed, 19 insertions(+), 67 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 2e9df02af86..8227a7be56f 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -126,9 +126,6 @@ static const UINT button_up_data[NB_BUTTONS] = XContext cursor_context = 0; -static HWND cursor_window; -static HCURSOR last_cursor; -static DWORD last_cursor_change; static RECT last_clip_rect; static HWND last_clip_foreground_window; static BOOL last_clip_refused; @@ -242,24 +239,6 @@ void set_window_cursor( Window window, HCURSOR handle ) XFlush( gdi_display ); } -/*********************************************************************** - * sync_window_cursor - */ -void sync_window_cursor( Window window ) -{ - HCURSOR cursor; - - SERVER_START_REQ( set_cursor ) - { - req->flags = 0; - wine_server_call( req ); - cursor = reply->prev_count >= 0 ? wine_server_ptr_handle( reply->prev_handle ) : 0; - } - SERVER_END_REQ; - - set_window_cursor( window, cursor ); -} - struct mouse_button_mapping { int deviceid; @@ -444,6 +423,7 @@ static BOOL grab_clipping_window( const RECT *clip ) UNICODE_STRING class_name = RTL_CONSTANT_STRING( messageW ); Window clip_window; HWND msg_hwnd = 0; + HCURSOR cursor; POINT pos; RECT real_clip; @@ -504,6 +484,17 @@ static BOOL grab_clipping_window( const RECT *clip ) GrabModeAsync, GrabModeAsync, clip_window, None, CurrentTime )) clipping_cursor = TRUE; + SERVER_START_REQ( set_cursor ) + { + req->flags = 0; + wine_server_call( req ); + if (reply->prev_count < 0) cursor = 0; + else cursor = wine_server_ptr_handle( reply->prev_handle ); + } + SERVER_END_REQ; + + set_window_cursor( clip_window, cursor ); + if (!clipping_cursor) { X11DRV_XInput2_Enable( data->display, None, 0 ); @@ -511,8 +502,6 @@ static BOOL grab_clipping_window( const RECT *clip ) return FALSE; } clip_rect = *clip; - if (!data->clip_hwnd) sync_window_cursor( clip_window ); - InterlockedExchangePointer( (void **)&cursor_window, msg_hwnd ); data->clip_hwnd = msg_hwnd; return TRUE; #else @@ -634,7 +623,6 @@ static void map_event_coords( HWND hwnd, Window window, Window event_root, int x static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPUT *input ) { struct x11drv_win_data *data; - Window win = 0; input->type = INPUT_MOUSE; @@ -645,25 +633,12 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU if (!clip_hwnd) return; if (thread_data->clip_window != window) return; - if (InterlockedExchangePointer( (void **)&cursor_window, clip_hwnd ) != clip_hwnd || - input->u.mi.time - last_cursor_change > 100) - { - sync_window_cursor( window ); - last_cursor_change = input->u.mi.time; - } __wine_send_input( hwnd, input, NULL ); return; } if (!(data = get_win_data( hwnd ))) return; - win = data->whole_window; release_win_data( data ); - if (InterlockedExchangePointer( (void **)&cursor_window, hwnd ) != hwnd || - input->u.mi.time - last_cursor_change > 100) - { - sync_window_cursor( win ); - last_cursor_change = input->u.mi.time; - } /* update the wine server Z-order */ @@ -1484,13 +1459,15 @@ void X11DRV_DestroyCursorIcon( HCURSOR handle ) */ void X11DRV_SetCursor( HWND hwnd, HCURSOR handle ) { - if (InterlockedExchangePointer( (void **)&last_cursor, handle ) != handle || - NtGetTickCount() - last_cursor_change > 100) + struct x11drv_win_data *data; + + if ((data = get_win_data( hwnd ))) { - last_cursor_change = NtGetTickCount(); - if (cursor_window) send_notify_message( cursor_window, WM_X11DRV_SET_CURSOR, - GetCurrentThreadId(), (LPARAM)handle ); + set_window_cursor( data->whole_window, handle ); + release_win_data( data ); } + + if (clipping_cursor) set_window_cursor( x11drv_thread_data()->clip_window, handle ); } /*********************************************************************** diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ddf396d4556..61548c4d5ca 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2089,8 +2089,6 @@ static void create_whole_window( struct x11drv_win_data *data ) XFlush( data->display ); /* make sure the window exists before we start painting to it */ - sync_window_cursor( data->whole_window ); - done: if (win_rgn) NtGdiDeleteObjectApp( win_rgn ); } @@ -3721,27 +3719,6 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) release_win_data( data ); } return 0; - case WM_X11DRV_SET_CURSOR: - { - Window win = 0; - - if ((data = get_win_data( hwnd ))) - { - win = data->whole_window; - release_win_data( data ); - } - else if (hwnd == x11drv_thread_data()->clip_hwnd) - win = x11drv_thread_data()->clip_window; - - if (win) - { - if (wp == GetCurrentThreadId()) - set_window_cursor( win, (HCURSOR)lp ); - else - sync_window_cursor( win ); - } - return 0; - } case WM_X11DRV_DELETE_TAB: taskbar_delete_tab( hwnd ); return 0; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 393bf5df17f..3f572343357 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -606,7 +606,6 @@ enum x11drv_window_messages WM_X11DRV_UPDATE_CLIPBOARD = 0x80001000, WM_X11DRV_SET_WIN_REGION, WM_X11DRV_DESKTOP_RESIZED, - WM_X11DRV_SET_CURSOR, WM_X11DRV_DELETE_TAB, WM_X11DRV_ADD_TAB }; @@ -733,7 +732,6 @@ extern XContext cursor_context DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; -extern void sync_window_cursor( Window window ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; extern void X11DRV_InitKeyboard( Display *display ) DECLSPEC_HIDDEN; From 4add7293cac90ccd34bc776855c2204af50c3b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 14 Jun 2023 19:25:40 +0200 Subject: [PATCH 530/758] server: Update the DF_WINE_CREATE_DESKTOP desktop flag on opening. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55047 (cherry picked from commit f4cb3230d84042075961b95178d3e1ac4bc49271) --- server/winstation.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/winstation.c b/server/winstation.c index 9e5d3871a6f..d9617b5509e 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -30,6 +30,7 @@ #include "winbase.h" #include "winuser.h" #include "winternl.h" +#include "ntuser.h" #include "object.h" #include "handle.h" @@ -271,7 +272,11 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned return NULL; } } - else clear_error(); + else + { + desktop->flags |= (flags & DF_WINE_CREATE_DESKTOP); + clear_error(); + } } return desktop; } From 2dc786a36d5e4f0497ef86abb557b00b6adaf643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 14 Jun 2023 19:27:59 +0200 Subject: [PATCH 531/758] win32u: NtUserGetObjectInformation returns a BOOL, not NTSTATUS. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55047 (cherry picked from commit b3c1bd33e0c913def8f214baf7875f2ef8a46e6e) --- dlls/win32u/winstation.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index f0190d2335e..9ddc67fcf8f 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -44,12 +44,9 @@ BOOL is_virtual_desktop(void) { HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); USEROBJECTFLAGS flags = {0}; - NTSTATUS status; DWORD len; - status = NtUserGetObjectInformation( desktop, UOI_FLAGS, &flags, sizeof(flags), &len ); - if (status) return FALSE; - + if (!NtUserGetObjectInformation( desktop, UOI_FLAGS, &flags, sizeof(flags), &len )) return FALSE; return !!(flags.dwFlags & DF_WINE_CREATE_DESKTOP); } From e83cc798bc3fe3082ab4004da9917be48eb082e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 15 Jun 2023 09:34:53 +0200 Subject: [PATCH 532/758] winex11: Don't grab the cursor if another process is focused. This might be the case when in virtual desktop mode for instance, where we don't change Wine foreground window when the virtual desktop is focused out. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55046 (cherry picked from commit 89415925b7a43d11317c5ffc27ef2d29c20d0970) --- dlls/winex11.drv/event.c | 24 +++++++++++++----------- dlls/winex11.drv/mouse.c | 6 ++++-- dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 45cf20aa0b2..d97dee8faf5 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -858,6 +858,18 @@ static const char * const focus_modes[] = "NotifyWhileGrabbed" }; +BOOL is_current_process_focused(void) +{ + Display *display = x11drv_thread_data()->display; + Window focus; + int revert; + HWND hwnd; + + XGetInputFocus( display, &focus, &revert ); + if (focus && !XFindContext( display, focus, winContext, (char **)&hwnd )) return TRUE; + return FALSE; +} + /********************************************************************** * X11DRV_FocusIn */ @@ -911,9 +923,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) */ static void focus_out( Display *display , HWND hwnd ) { - HWND hwnd_tmp; - Window focus_win; - int revert; struct x11drv_win_data *data; if (xim_in_compose_mode()) return; @@ -953,14 +962,7 @@ static void focus_out( Display *display , HWND hwnd ) /* don't reset the foreground window, if the window which is getting the focus is a Wine window */ - XGetInputFocus( display, &focus_win, &revert ); - if (focus_win) - { - if (XFindContext( display, focus_win, winContext, (char **)&hwnd_tmp ) != 0) - focus_win = 0; - } - - if (!focus_win) + if (!is_current_process_focused()) { /* Abey : 6-Oct-99. Check again if the focus out window is the Foreground window, because in most cases the messages sent diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 8227a7be56f..dd8fb02ba90 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -427,8 +427,10 @@ static BOOL grab_clipping_window( const RECT *clip ) POINT pos; RECT real_clip; - if (NtUserGetWindowThread( NtUserGetDesktopWindow(), NULL ) == GetCurrentThreadId()) - return TRUE; /* don't clip in the desktop process */ + /* don't clip in the desktop process */ + if (NtUserGetWindowThread( NtUserGetDesktopWindow(), NULL ) == GetCurrentThreadId()) return TRUE; + /* don't clip the cursor if the X input focus is on another process window */ + if (!is_current_process_focused()) return TRUE; if (!data) return FALSE; if (!(clip_window = init_clip_window())) return TRUE; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 3f572343357..83a9fe31bab 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -730,6 +730,7 @@ extern XContext winContext DECLSPEC_HIDDEN; /* X context to associate an X cursor to a Win32 cursor handle */ extern XContext cursor_context DECLSPEC_HIDDEN; +extern BOOL is_current_process_focused(void) DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; From fdf5bc8f0e27001be33f8e7eb7b9613458e46b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 15 Jun 2023 08:53:08 +0200 Subject: [PATCH 533/758] winex11: Simplify the cursor clipping retry mechanism. If the focus changes between Wine windows, the wineserver logic will decide to reset the clipping rectangle. However winex11 also needs to support the case when focus changes to a host window, in virtual desktop mode, and in this case the foreground window doesn't actually change. To fix this, in virtual desktop mode, release the cursor on focus out events, and reapply the cursor clipping rect when the virtual desktop window is focused again. We can use the same logic on NotifyGrab events, when the WM grabs the keyboard, and later reapply the Wine clipping rect when we are notified about the keyboard ungrab. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55046 (cherry picked from commit 1f90d03b7888f902d2aa6101bd60aa47766071cd) --- dlls/winex11.drv/event.c | 60 ++++++++++----------------------------- dlls/winex11.drv/mouse.c | 22 ++++---------- dlls/winex11.drv/x11drv.h | 1 + 3 files changed, 21 insertions(+), 62 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index d97dee8faf5..374394a46aa 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -876,31 +876,23 @@ BOOL is_current_process_focused(void) static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) { XFocusChangeEvent *event = &xev->xfocus; + BOOL was_grabbed; if (!hwnd) return FALSE; TRACE( "win %p xwin %lx detail=%s mode=%s\n", hwnd, event->window, focus_details[event->detail], focus_modes[event->mode] ); if (event->detail == NotifyPointer) return FALSE; + /* when focusing in the virtual desktop window, re-apply the cursor clipping rect */ + if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); if (hwnd == NtUserGetDesktopWindow()) return FALSE; - switch (event->mode) - { - case NotifyGrab: - /* these are received when moving undecorated managed windows on mutter */ - keyboard_grabbed = TRUE; - return FALSE; - case NotifyWhileGrabbed: - keyboard_grabbed = TRUE; - break; - case NotifyNormal: - keyboard_grabbed = FALSE; - break; - case NotifyUngrab: - keyboard_grabbed = FALSE; - retry_grab_clipping_window(); - return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - } + /* when keyboard grab is released, re-apply the cursor clipping rect */ + was_grabbed = keyboard_grabbed; + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; + if (was_grabbed > keyboard_grabbed) retry_grab_clipping_window(); + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ + if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; xim_set_focus( hwnd, TRUE ); @@ -950,11 +942,7 @@ static void focus_out( Display *display , HWND hwnd ) x11drv_thread_data()->last_focus = hwnd; xim_set_focus( hwnd, FALSE ); - if (is_virtual_desktop()) - { - if (hwnd == NtUserGetDesktopWindow()) NtUserClipCursor( NULL ); - return; - } + if (is_virtual_desktop()) return; if (hwnd != NtUserGetForegroundWindow()) return; if (!(NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_MINIMIZE)) send_message( hwnd, WM_CANCELMODE, 0, 0 ); @@ -994,29 +982,11 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) } if (!hwnd) return FALSE; - switch (event->mode) - { - case NotifyUngrab: - /* these are received when moving undecorated managed windows on mutter */ - keyboard_grabbed = FALSE; - return FALSE; - case NotifyNormal: - keyboard_grabbed = FALSE; - break; - case NotifyWhileGrabbed: - keyboard_grabbed = TRUE; - break; - case NotifyGrab: - keyboard_grabbed = TRUE; - - /* This will do nothing due to keyboard_grabbed == TRUE, but it - * will save the current clipping rect so we can restore it on - * FocusIn with NotifyUngrab mode. - */ - retry_grab_clipping_window(); - - return TRUE; /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ - } + /* in virtual desktop mode or when keyboard is grabbed, release any cursor grab but keep the clipping rect */ + keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; + if (is_virtual_desktop() || keyboard_grabbed) ungrab_clipping_window(); + /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */ + if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE; focus_out( event->display, hwnd ); return TRUE; diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index dd8fb02ba90..63f7655be9e 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -126,9 +126,6 @@ static const UINT button_up_data[NB_BUTTONS] = XContext cursor_context = 0; -static RECT last_clip_rect; -static HWND last_clip_foreground_window; -static BOOL last_clip_refused; static RECT clip_rect; static Cursor create_cursor( HANDLE handle ); @@ -443,15 +440,8 @@ static BOOL grab_clipping_window( const RECT *clip ) if (keyboard_grabbed) { WARN( "refusing to clip to %s\n", wine_dbgstr_rect(clip) ); - last_clip_refused = TRUE; - last_clip_foreground_window = NtUserGetForegroundWindow(); - last_clip_rect = *clip; return FALSE; } - else - { - last_clip_refused = FALSE; - } /* enable XInput2 unless we are already clipping */ if (!data->clip_hwnd) X11DRV_XInput2_Enable( data->display, None, PointerMotionMask ); @@ -517,7 +507,7 @@ static BOOL grab_clipping_window( const RECT *clip ) * * Release the pointer grab on the clip window. */ -static void ungrab_clipping_window(void) +void ungrab_clipping_window(void) { struct x11drv_thread_data *data = x11drv_init_thread_data(); Window clip_window = init_clip_window(); @@ -536,15 +526,13 @@ static void ungrab_clipping_window(void) /*********************************************************************** * retry_grab_clipping_window * - * Restore the current clip rectangle or retry the last one if it has - * been refused because of an active keyboard grab. + * Restore the current clip rectangle. */ void retry_grab_clipping_window(void) { - if (clipping_cursor) - NtUserClipCursor( &clip_rect ); - else if (last_clip_refused && NtUserGetForegroundWindow() == last_clip_foreground_window) - NtUserClipCursor( &last_clip_rect ); + RECT rect; + NtUserGetClipCursor( &rect ); + NtUserClipCursor( &rect ); } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 83a9fe31bab..d9881f635c4 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -734,6 +734,7 @@ extern BOOL is_current_process_focused(void) DECLSPEC_HIDDEN; extern void X11DRV_SetFocus( HWND hwnd ) DECLSPEC_HIDDEN; extern void set_window_cursor( Window window, HCURSOR handle ) DECLSPEC_HIDDEN; extern void retry_grab_clipping_window(void) DECLSPEC_HIDDEN; +extern void ungrab_clipping_window(void) DECLSPEC_HIDDEN; extern void move_resize_window( HWND hwnd, int dir ) DECLSPEC_HIDDEN; extern void X11DRV_InitKeyboard( Display *display ) DECLSPEC_HIDDEN; extern void X11DRV_InitMouse( Display *display ) DECLSPEC_HIDDEN; From e5106a774a8998a4265e503bf9596c48e6fc7244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 14 Jun 2023 00:12:03 +0200 Subject: [PATCH 534/758] winex11: Replace the clipping message HWND with a BOOL flag. We don't need the window anymore, it was only used to send ClipCursor notifications. This improves cursor clipping performance a lot as it avoids re-creating a window every time. (cherry picked from commit 272f712b605174e946da1dc65f927a23ee92a572) --- dlls/winex11.drv/mouse.c | 27 +++++++-------------------- dlls/winex11.drv/x11drv.h | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 63f7655be9e..cb07e459c1c 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -415,11 +415,8 @@ void X11DRV_XInput2_Enable( Display *display, Window window, long event_mask ) static BOOL grab_clipping_window( const RECT *clip ) { #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H - static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0}; struct x11drv_thread_data *data = x11drv_thread_data(); - UNICODE_STRING class_name = RTL_CONSTANT_STRING( messageW ); Window clip_window; - HWND msg_hwnd = 0; HCURSOR cursor; POINT pos; RECT real_clip; @@ -432,11 +429,6 @@ static BOOL grab_clipping_window( const RECT *clip ) if (!data) return FALSE; if (!(clip_window = init_clip_window())) return TRUE; - if (!(msg_hwnd = NtUserCreateWindowEx( 0, &class_name, &class_name, NULL, 0, 0, 0, 0, 0, - HWND_MESSAGE, 0, NtCurrentTeb()->Peb->ImageBaseAddress, - NULL, 0, NULL, 0, FALSE ))) - return TRUE; - if (keyboard_grabbed) { WARN( "refusing to clip to %s\n", wine_dbgstr_rect(clip) ); @@ -444,11 +436,11 @@ static BOOL grab_clipping_window( const RECT *clip ) } /* enable XInput2 unless we are already clipping */ - if (!data->clip_hwnd) X11DRV_XInput2_Enable( data->display, None, PointerMotionMask ); + if (!data->clipping_cursor) X11DRV_XInput2_Enable( data->display, None, PointerMotionMask ); TRACE( "clipping to %s win %lx\n", wine_dbgstr_rect(clip), clip_window ); - if (!data->clip_hwnd) XUnmapWindow( data->display, clip_window ); + if (!data->clipping_cursor) XUnmapWindow( data->display, clip_window ); TRACE( "user clip rect %s\n", wine_dbgstr_rect( clip ) ); @@ -467,7 +459,7 @@ static BOOL grab_clipping_window( const RECT *clip ) XMapWindow( data->display, clip_window ); /* if the rectangle is shrinking we may get a pointer warp */ - if (!data->clip_hwnd || clip->left > clip_rect.left || clip->top > clip_rect.top || + if (!data->clipping_cursor || clip->left > clip_rect.left || clip->top > clip_rect.top || clip->right < clip_rect.right || clip->bottom < clip_rect.bottom) data->warp_serial = NextRequest( data->display ); @@ -490,11 +482,10 @@ static BOOL grab_clipping_window( const RECT *clip ) if (!clipping_cursor) { X11DRV_XInput2_Enable( data->display, None, 0 ); - NtUserDestroyWindow( msg_hwnd ); return FALSE; } clip_rect = *clip; - data->clip_hwnd = msg_hwnd; + data->clipping_cursor = TRUE; return TRUE; #else WARN( "XInput2 was not available at compile time\n" ); @@ -518,8 +509,7 @@ void ungrab_clipping_window(void) XUnmapWindow( data->display, clip_window ); if (clipping_cursor) XUngrabPointer( data->display, CurrentTime ); clipping_cursor = FALSE; - NtUserDestroyWindow( data->clip_hwnd ); - data->clip_hwnd = 0; + data->clipping_cursor = FALSE; X11DRV_XInput2_Enable( data->display, None, 0 ); } @@ -567,7 +557,7 @@ static void map_event_coords( HWND hwnd, Window window, Window event_root, int x if (!hwnd) { thread_data = x11drv_thread_data(); - if (!thread_data->clip_hwnd) return; + if (!thread_data->clipping_cursor) return; if (thread_data->clip_window != window) return; pt.x = clip_rect.left; pt.y = clip_rect.top; @@ -619,10 +609,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU if (!hwnd) { struct x11drv_thread_data *thread_data = x11drv_thread_data(); - HWND clip_hwnd = thread_data->clip_hwnd; - - if (!clip_hwnd) return; - if (thread_data->clip_window != window) return; + if (!thread_data->clipping_cursor || thread_data->clip_window != window) return; __wine_send_input( hwnd, input, NULL ); return; } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index d9881f635c4..480a2937f3a 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -392,7 +392,7 @@ struct x11drv_thread_data Window selection_wnd; /* window used for selection interactions */ unsigned long warp_serial; /* serial number of last pointer warp request */ Window clip_window; /* window used for cursor clipping */ - HWND clip_hwnd; /* message window stored in desktop while clipping is active */ + BOOL clipping_cursor; /* whether thread is currently clipping the cursor */ #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H XIValuatorClassInfo x_valuator; XIValuatorClassInfo y_valuator; From 5f8d33ea6da122e48d01e9ba1569e75fc00c3cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 08:42:52 +0200 Subject: [PATCH 535/758] win32u: Remove unnecessary set_cursor new_clip rect copy. (cherry picked from commit 56e2a08dae6ead7738488c0b33832abb7b3a91d1) --- dlls/win32u/input.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 61adcd1bff8..b55e89a3d7b 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2655,13 +2655,7 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) } else req->flags = SET_CURSOR_NOCLIP; - if ((ret = !wine_server_call( req ))) - { - new_rect.left = reply->new_clip.left; - new_rect.top = reply->new_clip.top; - new_rect.right = reply->new_clip.right; - new_rect.bottom = reply->new_clip.bottom; - } + ret = !wine_server_call( req ); } SERVER_END_REQ; From 5308c2c6ef7f6ca0de349d831e67e1361de107fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 08:54:43 +0200 Subject: [PATCH 536/758] server: Pass set_cursor flags in WM_WINE_CLIPCURSOR wparam. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55047 (cherry picked from commit ab9b99c4a51afa36fdbec950aee1abbdc2fe28a6) --- dlls/win32u/input.c | 6 +++--- dlls/win32u/message.c | 4 +++- dlls/win32u/sysparams.c | 2 +- dlls/win32u/win32u_private.h | 2 +- server/queue.c | 12 ++++++------ server/user.h | 3 ++- server/window.c | 2 +- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index b55e89a3d7b..70d84981e79 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2585,13 +2585,13 @@ BOOL get_clip_cursor( RECT *rect ) return TRUE; } -BOOL process_wine_clipcursor( HWND hwnd, BOOL empty, BOOL reset ) +BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) { struct user_thread_info *thread_info = get_user_thread_info(); RECT rect, virtual_rect = NtUserGetVirtualScreenRect(); - BOOL was_clipping; + BOOL was_clipping, empty = !!(flags & SET_CURSOR_NOCLIP); - TRACE( "hwnd %p, empty %u, reset %u\n", hwnd, empty, reset ); + TRACE( "hwnd %p, flags %#x, reset %u\n", hwnd, flags, reset ); if ((was_clipping = thread_info->clipping_cursor)) InterlockedDecrement( &clipping_cursor ); thread_info->clipping_cursor = FALSE; diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index eb700321708..8c9d96f8e43 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1287,7 +1287,9 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam ); } case WM_WINE_CLIPCURSOR: - if (wparam && lparam) return clip_fullscreen_window( hwnd, FALSE ); + /* non-hardware message, posted on display mode change to trigger fullscreen + clipping or to the desktop window to forcefully release the cursor grabs */ + if (!wparam) return clip_fullscreen_window( hwnd, FALSE ); return process_wine_clipcursor( hwnd, wparam, lparam ); case WM_WINE_SETCURSOR: FIXME( "Unexpected non-hardware WM_WINE_SETCURSOR message\n" ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index b1e058a2db3..c7abc193f81 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2706,7 +2706,7 @@ static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmod MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), SMTO_ABORTIFHUNG, 2000, FALSE ); /* post clip_fullscreen_window request to the foreground window */ - NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, TRUE, TRUE ); + NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, 0, 0 ); } return ret; diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 784bb9778ae..1d1317b113c 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -283,7 +283,7 @@ extern void toggle_caret( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL unregister_touch_window( HWND hwnd ) DECLSPEC_HIDDEN; extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL get_clip_cursor( RECT *rect ) DECLSPEC_HIDDEN; -extern BOOL process_wine_clipcursor( HWND hwnd, BOOL empty, BOOL reset ) DECLSPEC_HIDDEN; +extern BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) DECLSPEC_HIDDEN; extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) DECLSPEC_HIDDEN; /* menu.c */ diff --git a/server/queue.c b/server/queue.c index 0a7b5fa70aa..549d08cbcf5 100644 --- a/server/queue.c +++ b/server/queue.c @@ -597,7 +597,7 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig } /* set the cursor clip rectangle */ -void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int reset ) +void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, unsigned int flags, int reset ) { rectangle_t top_rect, new_rect; int x, y; @@ -624,17 +624,17 @@ void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int r SHARED_WRITE_END( &desktop->shared->seq ); /* request clip cursor rectangle reset to the desktop thread */ - if (reset) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, TRUE, FALSE ); + if (reset) post_desktop_message( desktop, WM_WINE_CLIPCURSOR, flags, FALSE ); /* notify foreground thread, of reset, or to apply new cursor clipping rect */ - queue_cursor_message( desktop, 0, WM_WINE_CLIPCURSOR, rect == NULL, reset ); + queue_cursor_message( desktop, 0, WM_WINE_CLIPCURSOR, flags, reset ); } /* change the foreground input and reset the cursor clip rect */ static void set_foreground_input( struct desktop *desktop, struct thread_input *input ) { if (desktop->foreground_input == input) return; - set_clip_rectangle( desktop, NULL, 1 ); + set_clip_rectangle( desktop, NULL, SET_CURSOR_NOCLIP, 1 ); desktop->foreground_input = input; SHARED_WRITE_BEGIN( &desktop->shared->seq ); desktop->shared->foreground_tid = input ? input->shared->tid : 0; @@ -3803,8 +3803,8 @@ DECL_HANDLER(set_cursor) } SHARED_WRITE_END( &input->shared->seq ); if (req->flags & SET_CURSOR_POS) set_cursor_pos( desktop, req->x, req->y ); - if (req->flags & SET_CURSOR_CLIP) set_clip_rectangle( desktop, &req->clip, 0 ); - if (req->flags & SET_CURSOR_NOCLIP) set_clip_rectangle( desktop, NULL, 0 ); + if (req->flags & SET_CURSOR_CLIP) set_clip_rectangle( desktop, &req->clip, req->flags, 0 ); + if (req->flags & SET_CURSOR_NOCLIP) set_clip_rectangle( desktop, NULL, SET_CURSOR_NOCLIP, 0 ); if (req->flags & (SET_CURSOR_HANDLE | SET_CURSOR_COUNT)) { diff --git a/server/user.h b/server/user.h index 347e84e10bc..d9e4c023e29 100644 --- a/server/user.h +++ b/server/user.h @@ -105,7 +105,8 @@ extern void queue_cleanup_window( struct thread *thread, user_handle_t win ); extern int init_thread_queue( struct thread *thread ); extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to ); extern void detach_thread_input( struct thread *thread_from ); -extern void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, int reset ); +extern void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, + unsigned int flags, int reset ); extern void post_message( user_handle_t win, unsigned int message, lparam_t wparam, lparam_t lparam ); extern void send_notify_message( user_handle_t win, unsigned int message, diff --git a/server/window.c b/server/window.c index 260b44c07a8..7220fd65e53 100644 --- a/server/window.c +++ b/server/window.c @@ -1831,7 +1831,7 @@ static void set_window_pos( struct window *win, struct window *previous, } /* reset cursor clip rectangle when the desktop changes size */ - if (win == win->desktop->top_window) set_clip_rectangle( win->desktop, NULL, 1 ); + if (win == win->desktop->top_window) set_clip_rectangle( win->desktop, NULL, SET_CURSOR_NOCLIP, 1 ); /* if the window is not visible, everything is easy */ if (!visible) return; From 5e4716224ed629013629fc6f3d8773879c95eb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 Jun 2023 23:34:53 +0200 Subject: [PATCH 537/758] win32u: Use a specific flag instead of shrinking the clip rect. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55047 (cherry picked from commit 8d2de5dbe1f317ba8f9f7e2f88fb3a0629e5e174) --- dlls/win32u/input.c | 22 ++++++++++++++++------ dlls/win32u/message.c | 2 +- dlls/win32u/sysparams.c | 2 +- include/wine/server_protocol.h | 1 + server/protocol.def | 1 + 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 70d84981e79..9f561b1749b 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2489,9 +2489,10 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) { struct user_thread_info *thread_info = get_user_thread_info(); MONITORINFO monitor_info = {.cbSize = sizeof(MONITORINFO)}; - RECT rect, virtual_rect = NtUserGetVirtualScreenRect(); + RECT rect; HMONITOR monitor; DWORD style; + BOOL ret; if (hwnd == NtUserGetDesktopWindow()) return FALSE; if (hwnd != NtUserGetForegroundWindow()) return FALSE; @@ -2516,11 +2517,20 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) if (is_virtual_desktop()) return FALSE; } - /* shrink the clipping rect to make sure it is not ignored for being fullscreen */ - if (EqualRect( &monitor_info.rcMonitor, &virtual_rect )) InflateRect( &monitor_info.rcMonitor, -1, -1 ); - TRACE( "win %p clipping fullscreen\n", hwnd ); - return NtUserClipCursor( &monitor_info.rcMonitor ); + + SERVER_START_REQ( set_cursor ) + { + req->flags = SET_CURSOR_CLIP | SET_CURSOR_FSCLIP; + req->clip.left = monitor_info.rcMonitor.left; + req->clip.top = monitor_info.rcMonitor.top; + req->clip.right = monitor_info.rcMonitor.right; + req->clip.bottom = monitor_info.rcMonitor.bottom; + ret = !wine_server_call( req ); + } + SERVER_END_REQ; + + return ret; } /********************************************************************** @@ -2608,7 +2618,7 @@ BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) get_clip_cursor( &rect ); intersect_rect( &rect, &rect, &virtual_rect ); if (EqualRect( &rect, &virtual_rect )) empty = TRUE; - if (empty) + if (empty && !(flags & SET_CURSOR_FSCLIP)) { /* if currently clipping, check if we should switch to fullscreen clipping */ if (was_clipping && clip_fullscreen_window( hwnd, TRUE )) return TRUE; diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 8c9d96f8e43..6a9e77f796c 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1289,7 +1289,7 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR case WM_WINE_CLIPCURSOR: /* non-hardware message, posted on display mode change to trigger fullscreen clipping or to the desktop window to forcefully release the cursor grabs */ - if (!wparam) return clip_fullscreen_window( hwnd, FALSE ); + if (wparam & SET_CURSOR_FSCLIP) return clip_fullscreen_window( hwnd, FALSE ); return process_wine_clipcursor( hwnd, wparam, lparam ); case WM_WINE_SETCURSOR: FIXME( "Unexpected non-hardware WM_WINE_SETCURSOR message\n" ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index c7abc193f81..ec6cd043852 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2706,7 +2706,7 @@ static LONG apply_display_settings( const WCHAR *devname, const DEVMODEW *devmod MAKELPARAM( current_mode.dmPelsWidth, current_mode.dmPelsHeight ), SMTO_ABORTIFHUNG, 2000, FALSE ); /* post clip_fullscreen_window request to the foreground window */ - NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, 0, 0 ); + NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_CLIPCURSOR, SET_CURSOR_FSCLIP, 0 ); } return ret; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 7a7b22c0883..2c8189b79cf 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5300,6 +5300,7 @@ struct set_cursor_reply #define SET_CURSOR_POS 0x04 #define SET_CURSOR_CLIP 0x08 #define SET_CURSOR_NOCLIP 0x10 +#define SET_CURSOR_FSCLIP 0x20 struct get_cursor_history_request diff --git a/server/protocol.def b/server/protocol.def index 9770db77706..cef5a16decb 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3764,6 +3764,7 @@ struct handle_info #define SET_CURSOR_POS 0x04 #define SET_CURSOR_CLIP 0x08 #define SET_CURSOR_NOCLIP 0x10 +#define SET_CURSOR_FSCLIP 0x20 /* Get the history of the 64 last cursor positions */ @REQ(get_cursor_history) From eb8c33068959b4268be6e9f27eed2cd01ba7075d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 20 Jun 2023 09:53:02 +0200 Subject: [PATCH 538/758] imm32/tests: Test how deleting a character can behave. (cherry picked from commit d1f9aae59973aae89a228a3f16a28d41470fea96) --- dlls/imm32/tests/imm32.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 4bdb6be4a31..081a54fd878 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -7213,6 +7213,20 @@ static void test_ga_na_da(void) /* These sequences have some additional WM_IME_NOTIFY messages with unknown wparam > IMN_PRIVATE */ struct ime_call complete_seq[] = { + /* G */ + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}}, + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"\u3131", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x3131, .lparam = GCS_COMPSTR|GCS_COMPATTR|CS_INSERTCHAR|CS_NOMOVECARET}, + }, + + { + .hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .comp = L"", .result = L"", + .message = {.msg = WM_IME_COMPOSITION, .wparam = 0x1b, .lparam = GCS_CURSORPOS|GCS_DELTASTART|GCS_COMPSTR|GCS_COMPATTR|GCS_COMPCLAUSE| + GCS_COMPREADSTR|GCS_COMPREADATTR|GCS_COMPREADCLAUSE}, + }, + {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_ENDCOMPOSITION, .wparam = 0, .lparam = 0}}, + /* G */ {.hkl = default_hkl, .himc = 0/*himc*/, .func = MSG_TEST_WIN, .message = {.msg = WM_IME_STARTCOMPOSITION, .wparam = 0, .lparam = 0}}, { @@ -7377,6 +7391,14 @@ static void test_ga_na_da(void) flush_events(); keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 ); + keybd_event( VK_BACK, 0x0e, 0, 0 ); + flush_events(); + keybd_event( VK_BACK, 0x0e, KEYEVENTF_KEYUP, 0 ); + + keybd_event( 'R', 0x13, 0, 0 ); + flush_events(); + keybd_event( 'R', 0x13, KEYEVENTF_KEYUP, 0 ); + keybd_event( 'K', 0x25, 0, 0 ); flush_events(); keybd_event( 'K', 0x25, KEYEVENTF_KEYUP, 0 ); From 74bae632c3b9e9c157bfc04a383af6f9907746c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 15 Jun 2023 14:56:35 +0200 Subject: [PATCH 539/758] imm32: Use offsets instead of lengths to decide on sending updates. The composition or result strings may be present, but with zero length. In which case we still want to send the messages, to indicate any change for instance whenever a character is deleted and strings become empty. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55055 (cherry picked from commit e5646191723fa4b5e4cb38187e1611a58ebc8695) --- dlls/imm32/ime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index b1ff6c54c4e..2717b198c60 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -561,7 +561,7 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, TRANSMSG status_msg = {.message = ime_set_composition_status( himc, !!compstr->dwCompStrOffset )}; if (status_msg.message) msgs->TransMsg[count++] = status_msg; - if (compstr->dwResultStrLen) + if (compstr->dwResultStrOffset) { const WCHAR *result = (WCHAR *)((BYTE *)compstr + compstr->dwResultStrOffset); TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = result[0], .lParam = GCS_RESULTSTR}; @@ -569,7 +569,7 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, msgs->TransMsg[count++] = msg; } - if (compstr->dwCompStrLen) + if (compstr->dwCompStrOffset) { const WCHAR *comp = (WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset); TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = comp[0], .lParam = GCS_COMPSTR | GCS_CURSORPOS | GCS_DELTASTART}; From 770f6fe551efd6f1a9d3edb7c1e8e7c97cba5622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 13 Jun 2023 07:14:33 +0200 Subject: [PATCH 540/758] winex11: Report empty preedit string when result string is committed. Based on a patch from Byeong-Sik Jeon . Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55027 (cherry picked from commit 97c0a52ae552ed856c0955e8badfe0ddf88be91d) --- dlls/winex11.drv/xim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 1c3d2dd9875..1b063cfe886 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -140,7 +140,7 @@ void xim_set_result_string( HWND hwnd, const char *str, UINT count ) len = ntdll_umbstowcs( str, count, output, count ); output[len] = 0; - post_ime_update( hwnd, 0, ime_comp_buf, output ); + post_ime_update( hwnd, 0, NULL, output ); free( output ); } From ca3330f14b3b16a9ef0e11f20e5cbebe94718726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 30 May 2023 13:16:39 +0200 Subject: [PATCH 541/758] winex11: Make sure HIMC is opened before sending IME updates. (cherry picked from commit f7e76184f1c5064604381cdfc3c30ef357955e52) --- dlls/winex11.drv/xim.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 1b063cfe886..209d63f0402 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -175,6 +175,7 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg ) if ((ime_comp_buf = realloc( ime_comp_buf, sizeof(WCHAR) ))) *ime_comp_buf = 0; else ERR( "Failed to allocate preedit buffer\n" ); + NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, TRUE ); post_ime_update( hwnd, 0, ime_comp_buf, NULL ); return -1; @@ -190,6 +191,7 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) ime_comp_buf = NULL; post_ime_update( hwnd, 0, NULL, NULL ); + NtUserPostMessage( hwnd, WM_IME_NOTIFY, IMN_WINE_SET_OPEN_STATUS, FALSE ); return 0; } From b121795fff5d1f9dcae4b12ff1132317b6e76793 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 17 Oct 2018 19:55:27 +0300 Subject: [PATCH 542/758] winex11.drv: Ignore clip_reset when trying to clip the mouse after the desktop has been resized. This fixes the mouse clipping when the desktop is resized multiple times in a row. CW-Bug-Id: #21879 --- dlls/win32u/input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 9f561b1749b..5c9825a1ce8 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2505,7 +2505,8 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ) if (!NtUserGetWindowRect( hwnd, &rect )) return FALSE; if (!NtUserIsWindowRectFullScreen( &rect )) return FALSE; - if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; + + if (!reset && NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE; if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */ if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE; From 2c81b5cc7eaa34c69f479f5c4bf83d6082803f5b Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Sat, 20 Oct 2018 18:07:12 +0300 Subject: [PATCH 543/758] winex11.drv: Enable fullscreen clipping even if not already clipping. CW-Bug-Id: #21879 --- dlls/win32u/input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 5c9825a1ce8..800635e3753 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2600,11 +2600,11 @@ BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) { struct user_thread_info *thread_info = get_user_thread_info(); RECT rect, virtual_rect = NtUserGetVirtualScreenRect(); - BOOL was_clipping, empty = !!(flags & SET_CURSOR_NOCLIP); + BOOL empty = !!(flags & SET_CURSOR_NOCLIP); TRACE( "hwnd %p, flags %#x, reset %u\n", hwnd, flags, reset ); - if ((was_clipping = thread_info->clipping_cursor)) InterlockedDecrement( &clipping_cursor ); + if (thread_info->clipping_cursor) InterlockedDecrement( &clipping_cursor ); thread_info->clipping_cursor = FALSE; if (reset) @@ -2622,7 +2622,7 @@ BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) if (empty && !(flags & SET_CURSOR_FSCLIP)) { /* if currently clipping, check if we should switch to fullscreen clipping */ - if (was_clipping && clip_fullscreen_window( hwnd, TRUE )) return TRUE; + if (clip_fullscreen_window( hwnd, TRUE )) return TRUE; return user_driver->pClipCursor( NULL, FALSE ); } From e991913eb348a873e1745f785e797483e9b4fd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 21 Jan 2020 21:05:05 +0100 Subject: [PATCH 544/758] winex11.drv: Ignore ClipCursor if desktop window is foreground. CW-Bug-Id: #21879 --- dlls/win32u/input.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 800635e3753..b6f6883621a 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2643,6 +2643,12 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) TRACE( "Clipping to %s\n", wine_dbgstr_rect(rect) ); + if (NtUserGetForegroundWindow() == NtUserGetDesktopWindow()) + { + WARN( "desktop is foreground, ignoring ClipCursor\n" ); + rect = NULL; + } + if (rect) { if (rect->left > rect->right || rect->top > rect->bottom) return FALSE; From b5ae741bc97e4b1176c3626f17cb8750d60b3fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 19 Sep 2019 15:42:09 +0200 Subject: [PATCH 545/758] winex11.drv: Wait for pointer grab on FocusIn/WM_TAKE_FOCUS events. The FocusIn/WM_TAKE_FOCUS events are received as soon as a window is clicked, but when some modifier key is pressed or when the click is on the window frame, the WM may still be controlling the window size or position. It usually grabs the cursor while doing so - and if not then there's apparently nothing we can do. When using undecorated mode we handle this case "correctly" by going through the corresponding Windows non-client message loop until mouse buttons are released, but when using decorated windows the window decoration is empty from the Wine perspective and any window event is considered as happening in the client area. This leads to some issues when the window is moved or resized, with applications applying clipping rectangles immediately and not updating it on subsequent window move/resize messages. Delaying the WM_ACTIVATE until the WM releases its grab and the window move is complete helps solving this situation. This delay is implemented here by resending the FocusIn/WM_TAKE_FOCUS events to the window until the cursor can be grabbed and then processing them normally. winex11.drv: Fix focus delay issues with desktop clipping. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 374394a46aa..a3954db4e8c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -337,6 +337,27 @@ static Bool filter_event( Display *display, XEvent *event, char *arg ) } } +static void wait_grab_pointer( Display *display ) +{ + RECT rect; + + /* release cursor grab held by any Wine process */ + NtUserGetClipCursor( &rect ); + NtUserClipCursor( NULL ); + + while (XGrabPointer( display, root_window, False, 0, GrabModeAsync, GrabModeAsync, + None, None, CurrentTime ) != GrabSuccess) + { + LARGE_INTEGER timeout = {.QuadPart = -10 * (ULONGLONG)10000}; + NtDelayExecution( FALSE, &timeout ); + } + + XUngrabPointer( display, CurrentTime ); + XFlush( display ); + + /* restore the previously used clipping rect */ + NtUserClipCursor( &rect ); +} enum event_merge_action { @@ -685,6 +706,8 @@ static void set_focus( Display *display, HWND hwnd, Time time ) Window win; GUITHREADINFO threadinfo; + wait_grab_pointer( display ); + TRACE( "setting foreground window to %p\n", hwnd ); NtUserSetForegroundWindow( hwnd ); From 63ee0e65171d6388fffdee3c002f83b32dbb1370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Dec 2019 16:58:07 +0100 Subject: [PATCH 546/758] Revert "winex11.drv: Only call XWarpPointer if we can get exclusive pointer grab." This reverts commit 74efb3e872aebf57a42d62b52e149ae26f320c9a. We are now only activating windows only once the window manager has released its grab, it should be safer to let them move the pointer around and this should not be needed anymore. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47771 CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index cb07e459c1c..5049d323fbe 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1461,23 +1461,10 @@ BOOL X11DRV_SetCursorPos( INT x, INT y ) return FALSE; } - if (!clipping_cursor && - XGrabPointer( data->display, root_window, False, - PointerMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, None, None, CurrentTime ) != GrabSuccess) - { - WARN( "refusing to warp pointer to %u, %u without exclusive grab\n", (int)pos.x, (int)pos.y ); - return FALSE; - } - TRACE( "real setting to %s\n", wine_dbgstr_point( &pos ) ); XWarpPointer( data->display, root_window, root_window, 0, 0, 0, 0, pos.x, pos.y ); data->warp_serial = NextRequest( data->display ); - - if (!clipping_cursor) - XUngrabPointer( data->display, CurrentTime ); - XNoOp( data->display ); XFlush( data->display ); /* avoids bad mouse lag in games that do their own mouse warping */ TRACE( "warped to (fake) %d,%d serial %lu\n", x, y, data->warp_serial ); From 92372a38414a99b9a83faa8c77083b13a3fb342b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 9 Dec 2019 20:28:20 +0100 Subject: [PATCH 547/758] HACK: mutter: winex11.drv: Add a bit of delay before restoring mouse grabs on FocusIn. CW-Bug-Id: #21879 --- dlls/winex11.drv/event.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index a3954db4e8c..5d15c9cb7bd 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -910,6 +910,17 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); if (hwnd == NtUserGetDesktopWindow()) return FALSE; + /* Focus was just restored but it can be right after super was + * pressed and gnome-shell needs a bit of time to respond and + * toggle the activity view. If we grab the cursor right away + * it will cancel it and super key will do nothing. + */ + if (event->mode == NotifyUngrab && wm_is_mutter(event->display)) + { + LARGE_INTEGER timeout = {.QuadPart = 100 * -10000}; + NtDelayExecution( FALSE, &timeout ); + } + /* when keyboard grab is released, re-apply the cursor clipping rect */ was_grabbed = keyboard_grabbed; keyboard_grabbed = event->mode == NotifyGrab || event->mode == NotifyWhileGrabbed; From ab4ffe234f0c4d9e83f4ceb81f784a82d32262aa Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 15 Mar 2021 12:01:25 -0500 Subject: [PATCH 548/758] winex11.drv: Flush X connection after ungrabbing the pointer. CW-Bug-Id: #18169 CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 5049d323fbe..52ff8eddc25 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -507,7 +507,11 @@ void ungrab_clipping_window(void) TRACE( "no longer clipping\n" ); XUnmapWindow( data->display, clip_window ); - if (clipping_cursor) XUngrabPointer( data->display, CurrentTime ); + if (clipping_cursor) + { + XUngrabPointer( data->display, CurrentTime ); + XFlush( data->display ); + } clipping_cursor = FALSE; data->clipping_cursor = FALSE; X11DRV_XInput2_Enable( data->display, None, 0 ); From dc4e72e7c3412271aab71d18312312cba9a036ee Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 8 Jun 2021 16:01:54 +0300 Subject: [PATCH 549/758] winex11.drv: Send missed KEYUP events on KeymapNotify. Full focus lost / focus gained events on the Windows side are not feasible for X11's FocusIn/FocusOut events generated by keyboard grabs (see XGrabKeyboard()) that are used for example for Atl+Tab handling. Using them would degrade user's experience, especially with our full screen hack, by causing the window to minimize or flash multiple times depending on a game/window manager combo. Because of that the programs may miss on some KEYUP events that happen during the grab, and since there are no focus changes on the Windows side the state doesn't get resynced. This change attempts to improve user experience by syncing any missed key release events that happened while the window haven't had focus on the X11 side. There's no syncing of key presses as those are more problematic because of window manager quirks, e.g. on KDE it may end up syncing the Tab press portion of Alt+Tab. Luckily missing key events for keys that were pressed and not released while the WM had the keyboard grab is not nearly as confusing as stuck keys. For Warhammer: Chaosbane, theHunter: Call of the Wild, Far Cry Primal and many other games that end up with stuck Alt after Alt+Tabbing. CW-Bug-ID: #17046 CW-Bug-ID: #18904 --- dlls/winex11.drv/event.c | 2 ++ dlls/winex11.drv/keyboard.c | 43 +++++++++++++++++++++++++++++++++++-- dlls/winex11.drv/mouse.c | 2 ++ dlls/winex11.drv/x11drv.h | 1 + 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 5d15c9cb7bd..a7d4f4a2436 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -910,6 +910,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (is_virtual_desktop() && hwnd == NtUserGetDesktopWindow()) retry_grab_clipping_window(); if (hwnd == NtUserGetDesktopWindow()) return FALSE; + x11drv_thread_data()->keymapnotify_hwnd = hwnd; + /* Focus was just restored but it can be right after super was * pressed and gnome-shell needs a bit of time to respond and * toggle the activity view. If we grab the cursor right away diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 1d6cef40efb..f476919f9f5 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1195,11 +1195,19 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) int i, j; BYTE keystate[256]; WORD vkey; + DWORD flags; + KeyCode keycode; + HWND keymapnotify_hwnd; BOOL changed = FALSE; struct { WORD vkey; + WORD scan; WORD pressed; } keys[256]; + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + + keymapnotify_hwnd = thread_data->keymapnotify_hwnd; + thread_data->keymapnotify_hwnd = NULL; if (!get_async_key_state( keystate )) return FALSE; @@ -1214,11 +1222,17 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) { for (j = 0; j < 8; j++) { - vkey = keyc2vkey[(i * 8) + j]; + keycode = (i * 8) + j; + vkey = keyc2vkey[keycode]; /* If multiple keys map to the same vkey, we want to report it as * pressed iff any of them are pressed. */ - if (!keys[vkey & 0xff].vkey) keys[vkey & 0xff].vkey = vkey; + if (!keys[vkey & 0xff].vkey) + { + keys[vkey & 0xff].vkey = vkey; + keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff; + } + if (event->xkeymap.key_vector[i] & (1<window, event->x, event->y, event->detail ); + x11drv_thread_data()->keymapnotify_hwnd = hwnd; + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; /* simulate a mouse motion event */ diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 480a2937f3a..37112b80903 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -386,6 +386,7 @@ struct x11drv_thread_data XEvent *current_event; /* event currently being processed */ HWND grab_hwnd; /* window that currently grabs the mouse */ HWND last_focus; /* last window that had focus */ + HWND keymapnotify_hwnd; /* window that should receive modifier release events */ XIM xim; /* input method */ HWND last_xic_hwnd; /* last xic window */ XFontSet font_set; /* international text drawing font set */ From 7d26ca62671014f6a58c75d89d49aa8aa7828d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 26 Jun 2023 10:37:59 +0200 Subject: [PATCH 550/758] win32u: Keep the clipping rectangle inside a fullscreen window. CW-Bug-Id: #21707 --- dlls/win32u/input.c | 15 +++++++++++++-- dlls/win32u/sysparams.c | 2 +- dlls/win32u/win32u_private.h | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index b6f6883621a..c3ee888557a 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2637,13 +2637,14 @@ BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) */ BOOL WINAPI NtUserClipCursor( const RECT *rect ) { + HWND foreground = NtUserGetForegroundWindow(); UINT dpi; BOOL ret; - RECT new_rect; + RECT new_rect, full_rect; TRACE( "Clipping to %s\n", wine_dbgstr_rect(rect) ); - if (NtUserGetForegroundWindow() == NtUserGetDesktopWindow()) + if (foreground == NtUserGetDesktopWindow()) { WARN( "desktop is foreground, ignoring ClipCursor\n" ); rect = NULL; @@ -2658,6 +2659,16 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) new_rect = map_dpi_rect( *rect, dpi, get_monitor_dpi( monitor )); rect = &new_rect; } + + /* keep the mouse clipped inside of a fullscreen foreground window */ + if (NtUserGetWindowRect( foreground, &full_rect ) && is_window_rect_full_screen( &full_rect )) + { + full_rect.left = max( full_rect.left, min( full_rect.right - 1, rect->left ) ); + full_rect.right = max( full_rect.left, min( full_rect.right - 1, rect->right ) ); + full_rect.top = max( full_rect.top, min( full_rect.bottom - 1, rect->top ) ); + full_rect.bottom = max( full_rect.top, min( full_rect.bottom - 1, rect->bottom ) ); + rect = &full_rect; + } } SERVER_START_REQ( set_cursor ) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index ec6cd043852..055819d5aa0 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2048,7 +2048,7 @@ RECT get_virtual_screen_rect( UINT dpi ) return rect; } -static BOOL is_window_rect_full_screen( const RECT *rect ) +BOOL is_window_rect_full_screen( const RECT *rect ) { struct monitor *monitor; BOOL ret = FALSE; diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 1d1317b113c..3d1c7c99a43 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -351,6 +351,7 @@ extern int get_system_metrics( int index ) DECLSPEC_HIDDEN; extern UINT get_thread_dpi(void) DECLSPEC_HIDDEN; extern DPI_AWARENESS get_thread_dpi_awareness(void) DECLSPEC_HIDDEN; extern RECT get_virtual_screen_rect( UINT dpi ) DECLSPEC_HIDDEN; +extern BOOL is_window_rect_full_screen( const RECT *rect ) DECLSPEC_HIDDEN; extern BOOL is_exiting_thread( DWORD tid ) DECLSPEC_HIDDEN; extern POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN; extern RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN; From 3515f25b2d90bd8833bf5674c8c55c4ae05f3129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 26 Jun 2023 14:27:03 +0200 Subject: [PATCH 551/758] fixup! winex11.drv: Listen to RawMotion and RawButton* events in the desktop thread. --- dlls/winex11.drv/mouse.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index ec97ce51a36..dd362e18e3d 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -514,7 +514,10 @@ void ungrab_clipping_window(void) } clipping_cursor = FALSE; data->clipping_cursor = FALSE; - X11DRV_XInput2_Enable( data->display, None, 0 ); + + /* desktop window needs to listen to XInput2 events all the time for rawinput to work */ + if (NtUserGetWindowThread( NtUserGetDesktopWindow(), NULL ) != GetCurrentThreadId()) + X11DRV_XInput2_Enable( data->display, None, 0 ); } /*********************************************************************** From 3da1d20b123583055117cac934d949191dfdebb9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 26 Jun 2023 15:30:13 -0600 Subject: [PATCH 552/758] rsaenh: Store keys as volatile for SF6. CW-Bug-Id: #22347 --- dlls/rsaenh/rsaenh.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/rsaenh/rsaenh.c b/dlls/rsaenh/rsaenh.c index 3a2d7126ca4..3e4646a9517 100644 --- a/dlls/rsaenh/rsaenh.c +++ b/dlls/rsaenh/rsaenh.c @@ -1211,9 +1211,20 @@ static void store_key_permissions(HCRYPTKEY hCryptKey, HKEY hKey, DWORD dwKeySpe */ static BOOL create_container_key(KEYCONTAINER *pKeyContainer, REGSAM sam, HKEY *phKey) { + static DWORD key_options = ~0ul; CHAR szRSABase[sizeof(RSAENH_REGKEY) + MAX_PATH]; HKEY hRootKey; + if (key_options == ~0ul) + { + const char *sgi; + + if ((sgi = getenv("SteamGameId")) && !strcmp(sgi, "1364780")) + key_options = REG_OPTION_VOLATILE; + else + key_options = REG_OPTION_NON_VOLATILE; + } + sprintf(szRSABase, RSAENH_REGKEY, pKeyContainer->szName); if (pKeyContainer->dwFlags & CRYPT_MACHINE_KEYSET) @@ -1224,7 +1235,7 @@ static BOOL create_container_key(KEYCONTAINER *pKeyContainer, REGSAM sam, HKEY * /* @@ Wine registry key: HKLM\Software\Wine\Crypto\RSA */ /* @@ Wine registry key: HKCU\Software\Wine\Crypto\RSA */ return RegCreateKeyExA(hRootKey, szRSABase, 0, NULL, - REG_OPTION_NON_VOLATILE, sam, NULL, phKey, NULL) + key_options, sam, NULL, phKey, NULL) == ERROR_SUCCESS; } From ef2b700774a46069d756b0f5f36188c9a66501df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 27 Jun 2023 09:06:05 +0200 Subject: [PATCH 553/758] imm32: Avoid closing the input context on CPS_CANCEL. CW-Bug-Id: #22370 --- dlls/imm32/ime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 2717b198c60..0c6ed9c49e1 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -702,7 +702,8 @@ BOOL WINAPI NotifyIME( HIMC himc, DWORD action, DWORD index, DWORD value ) } case CPS_CANCEL: input_context_set_comp_str( ctx, NULL, 0 ); - ImmSetOpenStatus( himc, FALSE ); + if ((msg = ime_set_composition_status( himc, FALSE ))) ime_send_message( himc, msg, 0, 0 ); + NtUserNotifyIMEStatus( ctx->hWnd, FALSE ); break; default: FIXME( "himc %p, action %#lx, index %#lx, value %#lx stub!\n", himc, action, index, value ); From d03bdada2572c5a28b9d88e23beea190c7f8923b Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Wed, 28 Jun 2023 17:51:02 +0200 Subject: [PATCH 554/758] winevulkan: Keep deferred operation function params alive. The Vulkan spec says: Parameters to the command requesting a deferred operation may be accessed by the implementation at any time until the deferred operation enters the complete state. Pointer parameters must not be modified (e.g. reallocated/freed). This fixes a regression in Doom Eternal with ray tracing enabled with drivers that actually support deferred operations (e.g. nvidia, amdvlk). Link: https://gitlab.winehq.org/wine/wine/-/merge_requests/3188 --- dlls/winevulkan/make_vulkan | 31 ++++++++++++++++---- dlls/winevulkan/vulkan.c | 50 ++++++++++++++++++++++++++++++++ dlls/winevulkan/vulkan_private.h | 21 ++++++++++++++ 3 files changed, 96 insertions(+), 6 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 9de05b75662..f680c130955 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -269,6 +269,10 @@ FUNCTION_OVERRIDES = { "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC}, + # VK_KHR_deferred_host_operations + "vkCreateDeferredOperationKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, + "vkDestroyDeferredOperationKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, + # VK_EXT_calibrated_timestamps "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE}, @@ -834,6 +838,7 @@ class VkFunction(object): def body(self, conv, unwrap, params_prefix=""): body = "" needs_alloc = False + deferred_op = None # Declare any tmp parameters for conversion. for p in self.params: @@ -848,9 +853,12 @@ class VkFunction(object): body += " {0} {1}_host;\n".format(p.type, p.name) if p.needs_alloc(conv, unwrap): needs_alloc = True + if p.type == "VkDeferredOperationKHR" and not p.is_pointer(): + deferred_op = p.name if needs_alloc: - body += " struct conversion_context ctx;\n" + body += " struct conversion_context local_ctx;\n" + body += " struct conversion_context *ctx = &local_ctx;\n" body += "\n" if not self.is_perf_critical(): @@ -863,7 +871,13 @@ class VkFunction(object): body += " return STATUS_SUCCESS;\n\n" if needs_alloc: - body += " init_conversion_context(&ctx);\n" + if deferred_op is not None: + body += " if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op) + body += " " + body += " init_conversion_context(ctx);\n" + if deferred_op is not None: + body += " else\n" + body += " ctx = &wine_deferred_operation_from_handle(params->{})->ctx;\n".format(deferred_op) # Call any win_to_host conversion calls. unwrap = self.thunk_type == ThunkType.PUBLIC @@ -871,7 +885,7 @@ class VkFunction(object): if p.needs_conversion(conv, unwrap, Direction.INPUT): body += p.copy(Direction.INPUT, conv, unwrap, prefix=params_prefix) elif p.is_dynamic_array() and p.needs_conversion(conv, unwrap, Direction.OUTPUT): - body += " {0}_host = ({2}{0} && {1}) ? conversion_context_alloc(&ctx, sizeof(*{0}_host) * {1}) : NULL;\n".format( + body += " {0}_host = ({2}{0} && {1}) ? conversion_context_alloc(ctx, sizeof(*{0}_host) * {1}) : NULL;\n".format( p.name, p.get_dyn_array_len(params_prefix, conv), params_prefix) # Build list of parameters containing converted and non-converted parameters. @@ -900,7 +914,10 @@ class VkFunction(object): body += p.copy(Direction.OUTPUT, conv, unwrap, prefix=params_prefix) if needs_alloc: - body += " free_conversion_context(&ctx);\n" + if deferred_op is not None: + body += " if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op) + body += " " + body += " free_conversion_context(ctx);\n" # Finally return the result. Performance critical functions return void to allow tail calls. if not self.is_perf_critical(): @@ -1169,6 +1186,8 @@ class VkHandle(object): return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name) if self.name == "VkDebugReportCallbackEXT": return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name) + if self.name == "VkDeferredOperationKHR": + return "wine_deferred_operation_from_handle({0})->deferred_operation".format(name) if self.name == "VkDevice": return "wine_device_from_handle({0})->device".format(name) if self.name == "VkInstance": @@ -1795,7 +1814,7 @@ class VkParam(VkVariable): win_type = "win32" if conv else "win64" wrap_part = "" if unwrap or not self.needs_unwrapping() else "unwrapped_" if direction == Direction.INPUT: - ctx_param = "&ctx, " if self.needs_alloc(conv, unwrap) else "" + ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else "" if self.is_dynamic_array(): return " {0}_host = convert_{2}_array_{4}_to_{6}host({5}{1}, {3});\n".format( self.name, self.value(prefix, conv), self.type, self.get_dyn_array_len(prefix, conv), @@ -1803,7 +1822,7 @@ class VkParam(VkVariable): elif self.optional: ret = " if ({0}{1})\n".format(prefix, self.name) ret += " {\n" - ret += " {0}_host = conversion_context_alloc(&ctx, sizeof(*{0}_host));\n".format(self.name) + ret += " {0}_host = conversion_context_alloc(ctx, sizeof(*{0}_host));\n".format(self.name) ret += " convert_{0}_{3}_to_{5}host({4}{1}, {2}_host);\n".format( self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part) ret += " }\n" diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index c42f1472e16..845646e8343 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3200,6 +3200,56 @@ void wine_vkDestroyDebugReportCallbackEXT(VkInstance handle, VkDebugReportCallba free(object); } +VkResult wine_vkCreateDeferredOperationKHR(VkDevice handle, + const VkAllocationCallbacks* allocator, + VkDeferredOperationKHR* deferredOperation) +{ + struct wine_device *device = wine_device_from_handle(handle); + struct wine_deferred_operation *object; + VkResult res; + + if (allocator) + FIXME("Support for allocation callbacks not implemented yet\n"); + + if (!(object = calloc(1, sizeof(*object)))) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + res = device->funcs.p_vkCreateDeferredOperationKHR(device->device, NULL, &object->deferred_operation); + + if (res != VK_SUCCESS) + { + free(object); + return res; + } + + init_conversion_context(&object->ctx); + + WINE_VK_ADD_NON_DISPATCHABLE_MAPPING(device->phys_dev->instance, object, object->deferred_operation, object); + *deferredOperation = wine_deferred_operation_to_handle(object); + + return VK_SUCCESS; +} + +void wine_vkDestroyDeferredOperationKHR(VkDevice handle, + VkDeferredOperationKHR operation, + const VkAllocationCallbacks* allocator) +{ + struct wine_device *device = wine_device_from_handle(handle); + struct wine_deferred_operation *object; + + object = wine_deferred_operation_from_handle(operation); + + if (!object) + return; + + device->funcs.p_vkDestroyDeferredOperationKHR(device->device, object->deferred_operation, NULL); + + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, object); + + free_conversion_context(&object->ctx); + free(object); +} + void wine_vkDestroySwapchainKHR(VkDevice device_handle, VkSwapchainKHR handle, const VkAllocationCallbacks *allocator) { struct wine_device *device = wine_device_from_handle(device_handle); diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 245c81419a0..864fc392c2c 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -431,6 +431,27 @@ static inline void *conversion_context_alloc(struct conversion_context *pool, si } } +struct wine_deferred_operation +{ + VkDeferredOperationKHR deferred_operation; /* native handle */ + + struct conversion_context ctx; /* to keep params alive. */ + + struct wine_vk_mapping mapping; +}; + +static inline struct wine_deferred_operation *wine_deferred_operation_from_handle( + VkDeferredOperationKHR handle) +{ + return (struct wine_deferred_operation *)(uintptr_t)handle; +} + +static inline VkDeferredOperationKHR wine_deferred_operation_to_handle( + struct wine_deferred_operation *deferred_operation) +{ + return (VkDeferredOperationKHR)(uintptr_t)deferred_operation; +} + typedef UINT32 PTR32; typedef struct From 70959195cbbd3252ba3eb2c6bdd50878e9eed989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Jun 2022 12:51:22 +0200 Subject: [PATCH 555/758] windows.media.speech: Move constraints vector to the recognition session. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit e203d3be0ae022fae376bb6f1499dc875ad5c7b1) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index bdcc57f883e..105ed2b0b05 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -156,6 +156,8 @@ struct session ISpeechContinuousRecognitionSession ISpeechContinuousRecognitionSession_iface; LONG ref; + IVector_ISpeechRecognitionConstraint *constraints; + struct list completed_handlers; struct list result_handlers; }; @@ -208,6 +210,7 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface { typed_event_handlers_clear(&impl->completed_handlers); typed_event_handlers_clear(&impl->result_handlers); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -360,7 +363,6 @@ struct recognizer LONG ref; ISpeechContinuousRecognitionSession *session; - IVector_ISpeechRecognitionConstraint *constraints; }; /* @@ -424,7 +426,6 @@ static ULONG WINAPI recognizer_Release( ISpeechRecognizer *iface ) if (!ref) { ISpeechContinuousRecognitionSession_Release(impl->session); - IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -452,8 +453,11 @@ static HRESULT WINAPI recognizer_GetTrustLevel( ISpeechRecognizer *iface, TrustL static HRESULT WINAPI recognizer_get_Constraints( ISpeechRecognizer *iface, IVector_ISpeechRecognitionConstraint **vector ) { struct recognizer *impl = impl_from_ISpeechRecognizer(iface); + struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + TRACE("iface %p, operation %p.\n", iface, vector); - IVector_ISpeechRecognitionConstraint_AddRef((*vector = impl->constraints)); + + IVector_ISpeechRecognitionConstraint_AddRef((*vector = session->constraints)); return S_OK; } @@ -797,18 +801,22 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (language) FIXME("language parameter unused. Stub!\n"); + /* Init ISpeechContinuousRecognitionSession */ session->ISpeechContinuousRecognitionSession_iface.lpVtbl = &session_vtbl; session->ref = 1; + list_init(&session->completed_handlers); list_init(&session->result_handlers); + if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&session->constraints))) + goto error; + + /* Init ISpeechRecognizer */ impl->ISpeechRecognizer_iface.lpVtbl = &speech_recognizer_vtbl; impl->IClosable_iface.lpVtbl = &closable_vtbl; impl->ISpeechRecognizer2_iface.lpVtbl = &speech_recognizer2_vtbl; impl->session = &session->ISpeechContinuousRecognitionSession_iface; impl->ref = 1; - if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&impl->constraints))) - goto error; TRACE("created SpeechRecognizer %p.\n", impl); @@ -816,6 +824,7 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface return S_OK; error: + if (session->constraints) IVector_ISpeechRecognitionConstraint_Release(session->constraints); free(session); free(impl); From c25aa3220e57a6bd973672877ae7d4990e6e4134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 3 Jan 2023 15:08:28 +0100 Subject: [PATCH 556/758] windows.media.speech: Do not force calling convention on internal callbacks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 754452dd9d840a03c3996a53fb27bfe30089ddf7) CW-Bug-Id: #20134 --- dlls/windows.media.speech/private.h | 4 ++-- dlls/windows.media.speech/recognizer.c | 8 ++++---- dlls/windows.media.speech/synthesizer.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 13964329697..41f7b02e3de 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -69,8 +69,8 @@ struct vector_iids const GUID *view; }; -typedef HRESULT (WINAPI *async_action_callback)( IInspectable *invoker ); -typedef HRESULT (WINAPI *async_operation_inspectable_callback)( IInspectable *invoker, IInspectable **result ); +typedef HRESULT (*async_action_callback)( IInspectable *invoker ); +typedef HRESULT (*async_operation_inspectable_callback)( IInspectable *invoker, IInspectable **result ); HRESULT async_action_create( IInspectable *invoker, async_action_callback callback, IAsyncAction **out ); HRESULT async_operation_inspectable_create( const GUID *iid, IInspectable *invoker, async_operation_inspectable_callback callback, diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 105ed2b0b05..aaf9c5f8908 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -247,7 +247,7 @@ static HRESULT WINAPI session_set_AutoStopSilenceTimeout( ISpeechContinuousRecog return E_NOTIMPL; } -static HRESULT WINAPI start_callback( IInspectable *invoker ) +static HRESULT session_start_async( IInspectable *invoker ) { return S_OK; } @@ -255,7 +255,7 @@ static HRESULT WINAPI start_callback( IInspectable *invoker ) static HRESULT WINAPI session_StartAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { FIXME("iface %p, action %p stub!\n", iface, action); - return async_action_create(NULL, start_callback, action); + return async_action_create(NULL, session_start_async, action); } static HRESULT WINAPI session_StartWithModeAsync( ISpeechContinuousRecognitionSession *iface, @@ -479,7 +479,7 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } -static HRESULT WINAPI compile_callback( IInspectable *invoker, IInspectable **result ) +static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IInspectable **result ) { return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); } @@ -489,7 +489,7 @@ static HRESULT WINAPI recognizer_CompileConstraintsAsync( ISpeechRecognizer *ifa { IAsyncOperation_IInspectable **value = (IAsyncOperation_IInspectable **)operation; FIXME("iface %p, operation %p semi-stub!\n", iface, operation); - return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, compile_callback, value); + return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, recognizer_compile_constraints_async, value); } static HRESULT WINAPI recognizer_RecognizeAsync( ISpeechRecognizer *iface, diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index ce257c7c355..39d14b84ab7 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -375,7 +375,7 @@ static HRESULT WINAPI synthesizer_GetTrustLevel( ISpeechSynthesizer *iface, Trus return E_NOTIMPL; } -static HRESULT CALLBACK text_to_stream_operation( IInspectable *invoker, IInspectable **result ) +static HRESULT synthesizer_synthesize_text_to_stream_async( IInspectable *invoker, IInspectable **result ) { return synthesis_stream_create((ISpeechSynthesisStream **)result); } @@ -385,10 +385,10 @@ static HRESULT WINAPI synthesizer_SynthesizeTextToStreamAsync( ISpeechSynthesize { TRACE("iface %p, text %p, operation %p.\n", iface, text, operation); return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechSynthesisStream, NULL, - text_to_stream_operation, (IAsyncOperation_IInspectable **)operation); + synthesizer_synthesize_text_to_stream_async, (IAsyncOperation_IInspectable **)operation); } -static HRESULT CALLBACK ssml_to_stream_operation( IInspectable *invoker, IInspectable **result ) +static HRESULT synthesizer_synthesize_ssml_to_stream_async( IInspectable *invoker, IInspectable **result ) { return synthesis_stream_create((ISpeechSynthesisStream **)result); } @@ -398,7 +398,7 @@ static HRESULT WINAPI synthesizer_SynthesizeSsmlToStreamAsync( ISpeechSynthesize { TRACE("iface %p, ssml %p, operation %p.\n", iface, ssml, operation); return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechSynthesisStream, NULL, - ssml_to_stream_operation, (IAsyncOperation_IInspectable **)operation); + synthesizer_synthesize_ssml_to_stream_async, (IAsyncOperation_IInspectable **)operation); } static HRESULT WINAPI synthesizer_put_Voice( ISpeechSynthesizer *iface, IVoiceInformation *value ) From 2092575a8c3c010f45b803c17c4be7f73124f200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 3 May 2022 17:04:08 +0200 Subject: [PATCH 557/758] windows.media.speech: Return IAsyncAction from session_StopAsync. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit d21b872a8063494404d4fd9fca6f31610d33c89c) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 7 ++++++- dlls/windows.media.speech/tests/speech.c | 24 ++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index aaf9c5f8908..b4f68489bd0 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -266,10 +266,15 @@ static HRESULT WINAPI session_StartWithModeAsync( ISpeechContinuousRecognitionSe return E_NOTIMPL; } +static HRESULT session_stop_async( IInspectable *invoker ) +{ + return S_OK; +} + static HRESULT WINAPI session_StopAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { FIXME("iface %p, action %p stub!\n", iface, action); - return E_NOTIMPL; + return async_action_create(NULL, session_stop_async, action); } static HRESULT WINAPI session_CancelAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 445d10923ae..b6355743a83 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1762,9 +1762,7 @@ static void test_Recognition(void) */ hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action2); - todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); - - if (FAILED(hr)) goto skip_action; + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); async_void_handler_create_static(&action_handler); action_handler.event_block = CreateEventW(NULL, FALSE, FALSE, NULL); @@ -1776,40 +1774,38 @@ static void test_Recognition(void) put_param.handler = &action_handler.IAsyncActionCompletedHandler_iface; put_param.action = action2; put_thread = CreateThread(NULL, 0, action_put_completed_thread, &put_param, 0, NULL); - todo_wine ok(!WaitForSingleObject(action_handler.event_finished , 5000), "Wait for event_finished failed.\n"); + ok(!WaitForSingleObject(action_handler.event_finished , 5000), "Wait for event_finished failed.\n"); handler = (void *)0xdeadbeef; old_ref = action_handler.ref; hr = IAsyncAction_get_Completed(action2, &handler); - todo_wine ok(hr == S_OK, "IAsyncAction_get_Completed failed, hr %#lx.\n", hr); + ok(hr == S_OK, "IAsyncAction_get_Completed failed, hr %#lx.\n", hr); - todo_wine ok(handler == &action_handler.IAsyncActionCompletedHandler_iface || /* Broken on 1507. */ - broken(handler != NULL && handler != (void *)0xdeadbeef), "Handler was %p.\n", handler); + todo_wine ok(handler == &action_handler.IAsyncActionCompletedHandler_iface, "Handler was %p.\n", handler); ref = action_handler.ref - old_ref; todo_wine ok(ref == 1, "The ref was increased by %lu.\n", ref); - IAsyncActionCompletedHandler_Release(handler); + if (handler) IAsyncActionCompletedHandler_Release(handler); hr = IAsyncAction_QueryInterface(action2, &IID_IAsyncInfo, (void **)&info); - todo_wine ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); + ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); hr = IAsyncInfo_Close(info); /* If IAsyncInfo_Close would wait for the handler to finish, the test would get stuck here. */ - todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); + ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); check_async_info((IInspectable *)action2, 3, AsyncStatus_Closed, S_OK); set = SetEvent(action_handler.event_block); - todo_wine ok(set == TRUE, "Event 'event_block' wasn't set.\n"); - todo_wine ok(!WaitForSingleObject(put_thread , 1000), "Wait for put_thread failed.\n"); + ok(set == TRUE, "Event 'event_block' wasn't set.\n"); + ok(!WaitForSingleObject(put_thread , 1000), "Wait for put_thread failed.\n"); IAsyncInfo_Release(info); CloseHandle(action_handler.event_finished); CloseHandle(action_handler.event_block); CloseHandle(put_thread); - todo_wine ok(action != action2, "actions were the same!\n"); + ok(action != action2, "actions were the same!\n"); IAsyncAction_Release(action2); -skip_action: IAsyncAction_Release(action); hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); From e00daf582acd2c75609fb54f64c31becc4a9b946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 3 May 2022 20:04:11 +0200 Subject: [PATCH 558/758] windows.media.speech: Return IAsyncAction from session_PauseAsync. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 4e88f093b011ee70165d026a5233ce7b1445470e) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 7 ++++++- dlls/windows.media.speech/tests/speech.c | 11 ++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index b4f68489bd0..54a0e165f5f 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -283,10 +283,15 @@ static HRESULT WINAPI session_CancelAsync( ISpeechContinuousRecognitionSession * return E_NOTIMPL; } +static HRESULT session_pause_async( IInspectable *invoker ) +{ + return S_OK; +} + static HRESULT WINAPI session_PauseAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { FIXME("iface %p, action %p stub!\n", iface, action); - return E_NOTIMPL; + return async_action_create(NULL, session_pause_async, action); } static HRESULT WINAPI session_Resume( ISpeechContinuousRecognitionSession *iface ) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index b6355743a83..8b14221f487 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1761,6 +1761,15 @@ static void test_Recognition(void) * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. */ + hr = ISpeechContinuousRecognitionSession_PauseAsync(session, &action2); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); + await_async_void(action2, &action_handler); + check_async_info((IInspectable *)action2, 3, Completed, S_OK); + IAsyncAction_Release(action2); + + hr = ISpeechContinuousRecognitionSession_Resume(session); + todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); + hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action2); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); @@ -1792,7 +1801,7 @@ static void test_Recognition(void) hr = IAsyncInfo_Close(info); /* If IAsyncInfo_Close would wait for the handler to finish, the test would get stuck here. */ ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); - check_async_info((IInspectable *)action2, 3, AsyncStatus_Closed, S_OK); + check_async_info((IInspectable *)action2, 4, AsyncStatus_Closed, S_OK); set = SetEvent(action_handler.event_block); ok(set == TRUE, "Event 'event_block' wasn't set.\n"); From 1c323c5ec91901eda24a002949c27baea2553e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 16 May 2022 18:40:22 +0200 Subject: [PATCH 559/758] windows.media.speech/tests: Test the recognizer state. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit b36871b85a40c0da6c56f8d9f06303d42ce75095) CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 8b14221f487..155382fa76d 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1619,6 +1619,7 @@ static void test_Recognition(void) struct iterator_hstring iterator_hstring; struct iterable_hstring iterable_hstring; EventRegistrationToken token = { .value = 0 }; + SpeechRecognizerState recog_state; HSTRING commands[3], hstr, tag; HANDLE put_thread; LONG ref, old_ref; @@ -1717,6 +1718,11 @@ static void test_Recognition(void) ok(hr == S_OK, "ISpeechContinuousRecognitionSession_add_ResultGenerated failed, hr %#lx.\n", hr); ok(token.value != 0xdeadbeef, "Got unexpexted token: %#I64x.\n", token.value); + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + hr = ISpeechRecognizer_CompileConstraintsAsync(recognizer, &operation); ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr); await_async_inspectable((IAsyncOperation_IInspectable *)operation, @@ -1757,6 +1763,11 @@ static void test_Recognition(void) IAsyncInfo_Release(info); + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); + /* * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. */ @@ -1767,9 +1778,20 @@ static void test_Recognition(void) check_async_info((IInspectable *)action2, 3, Completed, S_OK); IAsyncAction_Release(action2); + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Paused || /* Broken on Win10 1507 */ + broken(recog_state == SpeechRecognizerState_Capturing) , "recog_state was %u.\n", recog_state); + hr = ISpeechContinuousRecognitionSession_Resume(session); todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); + hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action2); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); @@ -1817,6 +1839,11 @@ static void test_Recognition(void) IAsyncAction_Release(action2); IAsyncAction_Release(action); + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr); From 8ceb57a68377933a7f79f0a95f71f1d3ee3e9749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 3 Jan 2023 12:15:05 +0100 Subject: [PATCH 560/758] windows.media.speech/tests: Test starting, stopping, pausing and resuming the recognition session. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 86fb17c8fbffd8bcc039ca4412393d26cef73278) CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 155382fa76d..a8ed8cff1e7 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -18,6 +18,7 @@ #define COBJMACROS #include +#include "corerror.h" #include "windef.h" #include "winbase.h" #include "winerror.h" @@ -1741,6 +1742,11 @@ static void test_Recognition(void) await_async_void(action, &action_handler); + action2 = (void *)0xdeadbeef; + hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action2); + todo_wine ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); + todo_wine ok(action2 == NULL, "action2 was %p.\n", action2); + hr = IAsyncAction_QueryInterface(action, &IID_IAsyncInfo, (void **)&info); ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); check_async_info((IInspectable *)action, 1, Completed, S_OK); @@ -1784,6 +1790,17 @@ static void test_Recognition(void) todo_wine ok(recog_state == SpeechRecognizerState_Paused || /* Broken on Win10 1507 */ broken(recog_state == SpeechRecognizerState_Capturing) , "recog_state was %u.\n", recog_state); + /* Check what happens if we try to pause again, when the session is already paused. */ + hr = ISpeechContinuousRecognitionSession_PauseAsync(session, &action2); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); + await_async_void(action2, &action_handler); + check_async_info((IInspectable *)action2, 4, Completed, S_OK); + IAsyncAction_Release(action2); + + hr = ISpeechContinuousRecognitionSession_Resume(session); + todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); + + /* Resume when already resumed. */ hr = ISpeechContinuousRecognitionSession_Resume(session); todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); @@ -1823,7 +1840,7 @@ static void test_Recognition(void) hr = IAsyncInfo_Close(info); /* If IAsyncInfo_Close would wait for the handler to finish, the test would get stuck here. */ ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); - check_async_info((IInspectable *)action2, 4, AsyncStatus_Closed, S_OK); + check_async_info((IInspectable *)action2, 5, AsyncStatus_Closed, S_OK); set = SetEvent(action_handler.event_block); ok(set == TRUE, "Event 'event_block' wasn't set.\n"); @@ -1844,6 +1861,11 @@ static void test_Recognition(void) todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + /* Try stopping, when already stopped. */ + hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); + todo_wine ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); + todo_wine ok(action == NULL, "action was %p.\n", action); + hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr); From 2c6116fa76dacb6ce779ad802f26684dda608c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 3 Jan 2023 14:16:06 +0100 Subject: [PATCH 561/758] windows.media.speech: Add a worker thread to the recognition session. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 9515736a85c19344f2f5f2d1752cf0036be9129b) CW-Bug-Id: #20134 --- dlls/windows.media.speech/private.h | 1 + dlls/windows.media.speech/recognizer.c | 132 +++++++++++++++++++++-- dlls/windows.media.speech/tests/speech.c | 8 +- 3 files changed, 131 insertions(+), 10 deletions(-) diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 41f7b02e3de..e80d73ec1fb 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -23,6 +23,7 @@ #include #define COBJMACROS +#include "corerror.h" #include "windef.h" #include "winbase.h" #include "winstring.h" diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 54a0e165f5f..aabfd56694a 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -160,6 +160,10 @@ struct session struct list completed_handlers; struct list result_handlers; + + HANDLE worker_thread, worker_control_event; + BOOLEAN worker_running; + CRITICAL_SECTION cs; }; /* @@ -173,6 +177,31 @@ static inline struct session *impl_from_ISpeechContinuousRecognitionSession( ISp return CONTAINING_RECORD(iface, struct session, ISpeechContinuousRecognitionSession_iface); } +static DWORD CALLBACK session_worker_thread_cb( void *args ) +{ + ISpeechContinuousRecognitionSession *iface = args; + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + BOOLEAN running = TRUE; + DWORD status; + + SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker"); + + while (running) + { + status = WaitForMultipleObjects(1, &impl->worker_control_event, FALSE, INFINITE); + if (status == 0) /* worker_control_event signaled */ + { + EnterCriticalSection(&impl->cs); + running = impl->worker_running; + LeaveCriticalSection(&impl->cs); + } + + /* TODO: Send mic data to recognizer and handle results. */ + } + + return 0; +} + static HRESULT WINAPI session_QueryInterface( ISpeechContinuousRecognitionSession *iface, REFIID iid, void **out ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); @@ -208,8 +237,24 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface if (!ref) { + HANDLE thread; + + EnterCriticalSection(&impl->cs); + thread = impl->worker_thread; + impl->worker_running = FALSE; + impl->worker_thread = INVALID_HANDLE_VALUE; + LeaveCriticalSection(&impl->cs); + + SetEvent(impl->worker_control_event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + typed_event_handlers_clear(&impl->completed_handlers); typed_event_handlers_clear(&impl->result_handlers); + + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&impl->cs); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -254,8 +299,37 @@ static HRESULT session_start_async( IInspectable *invoker ) static HRESULT WINAPI session_StartAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { - FIXME("iface %p, action %p stub!\n", iface, action); - return async_action_create(NULL, session_start_async, action); + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + HRESULT hr; + + TRACE("iface %p, action %p.\n", iface, action); + + if (FAILED(hr = async_action_create(NULL, session_start_async, action))) + return hr; + + EnterCriticalSection(&impl->cs); + if (impl->worker_running || impl->worker_thread) + { + hr = COR_E_INVALIDOPERATION; + } + else if (!(impl->worker_thread = CreateThread(NULL, 0, session_worker_thread_cb, impl, 0, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + impl->worker_running = FALSE; + } + else + { + impl->worker_running = TRUE; + } + LeaveCriticalSection(&impl->cs); + + if (FAILED(hr)) + { + IAsyncAction_Release(*action); + *action = NULL; + } + + return hr; } static HRESULT WINAPI session_StartWithModeAsync( ISpeechContinuousRecognitionSession *iface, @@ -273,8 +347,45 @@ static HRESULT session_stop_async( IInspectable *invoker ) static HRESULT WINAPI session_StopAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { - FIXME("iface %p, action %p stub!\n", iface, action); - return async_action_create(NULL, session_stop_async, action); + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + HANDLE thread; + HRESULT hr; + + TRACE("iface %p, action %p.\n", iface, action); + + if (FAILED(hr = async_action_create(NULL, session_stop_async, action))) + return hr; + + EnterCriticalSection(&impl->cs); + if (impl->worker_running && impl->worker_thread) + { + thread = impl->worker_thread; + impl->worker_thread = INVALID_HANDLE_VALUE; + impl->worker_running = FALSE; + } + else + { + hr = COR_E_INVALIDOPERATION; + } + LeaveCriticalSection(&impl->cs); + + if (SUCCEEDED(hr)) + { + SetEvent(impl->worker_control_event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + EnterCriticalSection(&impl->cs); + impl->worker_thread = NULL; + LeaveCriticalSection(&impl->cs); + } + else + { + IAsyncAction_Release(*action); + *action = NULL; + } + + return hr; } static HRESULT WINAPI session_CancelAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) @@ -818,9 +929,18 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface list_init(&session->completed_handlers); list_init(&session->result_handlers); + if (!(session->worker_control_event = CreateEventW(NULL, FALSE, FALSE, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto error; + } + if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&session->constraints))) goto error; + InitializeCriticalSection(&session->cs); + session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); + /* Init ISpeechRecognizer */ impl->ISpeechRecognizer_iface.lpVtbl = &speech_recognizer_vtbl; impl->IClosable_iface.lpVtbl = &closable_vtbl; @@ -828,13 +948,13 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface impl->session = &session->ISpeechContinuousRecognitionSession_iface; impl->ref = 1; - TRACE("created SpeechRecognizer %p.\n", impl); - *speechrecognizer = &impl->ISpeechRecognizer_iface; + TRACE("created SpeechRecognizer %p.\n", *speechrecognizer); return S_OK; error: if (session->constraints) IVector_ISpeechRecognitionConstraint_Release(session->constraints); + if (session->worker_control_event) CloseHandle(session->worker_control_event); free(session); free(impl); diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index a8ed8cff1e7..8b31031d3c5 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1744,8 +1744,8 @@ static void test_Recognition(void) action2 = (void *)0xdeadbeef; hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action2); - todo_wine ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); - todo_wine ok(action2 == NULL, "action2 was %p.\n", action2); + ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); + ok(action2 == NULL, "action2 was %p.\n", action2); hr = IAsyncAction_QueryInterface(action, &IID_IAsyncInfo, (void **)&info); ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); @@ -1863,8 +1863,8 @@ static void test_Recognition(void) /* Try stopping, when already stopped. */ hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); - todo_wine ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); - todo_wine ok(action == NULL, "action was %p.\n", action); + ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); + ok(action == NULL, "action was %p.\n", action); hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr); From 025ff57a9a812efb47e262d8489e002c15483be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Sat, 14 Jan 2023 23:15:09 +0100 Subject: [PATCH 562/758] windows.media.speech/tests: Check if stopping the session resets the paused state. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit e1c4035efd9d916b09d141e6fb2012450f867f96) CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 50 ++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 8b31031d3c5..c358aa70927 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1787,8 +1787,8 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Paused || /* Broken on Win10 1507 */ - broken(recog_state == SpeechRecognizerState_Capturing) , "recog_state was %u.\n", recog_state); + todo_wine ok(recog_state == SpeechRecognizerState_Paused || + broken(recog_state == SpeechRecognizerState_Capturing) /* Broken on Win10 1507 */, "recog_state was %u.\n", recog_state); /* Check what happens if we try to pause again, when the session is already paused. */ hr = ISpeechContinuousRecognitionSession_PauseAsync(session, &action2); @@ -1844,7 +1844,7 @@ static void test_Recognition(void) set = SetEvent(action_handler.event_block); ok(set == TRUE, "Event 'event_block' wasn't set.\n"); - ok(!WaitForSingleObject(put_thread , 1000), "Wait for put_thread failed.\n"); + ok(!WaitForSingleObject(put_thread, 1000), "Wait for put_thread failed.\n"); IAsyncInfo_Release(info); CloseHandle(action_handler.event_finished); @@ -1866,6 +1866,50 @@ static void test_Recognition(void) ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); ok(action == NULL, "action was %p.\n", action); + /* Test, if Start/StopAsync resets the pause state. */ + hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); + await_async_void(action, &action_handler); + IAsyncAction_Release(action); + + hr = ISpeechContinuousRecognitionSession_PauseAsync(session, &action); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); + await_async_void(action, &action_handler); + IAsyncAction_Release(action); + + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Paused || + broken(recog_state == SpeechRecognizerState_Capturing) /* Broken on Win10 1507 */, "recog_state was %u.\n", recog_state); + + hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); + await_async_void(action, &action_handler); + IAsyncAction_Release(action); + + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + + hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); + await_async_void(action, &action_handler); + IAsyncAction_Release(action); + + recog_state = 0xdeadbeef; + hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); + todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + todo_wine ok(recog_state == SpeechRecognizerState_Capturing + || broken(recog_state == SpeechRecognizerState_Idle) /* Sometimes Windows is a little behind. */, + "recog_state was %u.\n", recog_state); + + hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); + await_async_void(action, &action_handler); + IAsyncAction_Release(action); + hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr); From df6206b89eb9841fc428104dce7abf6596a48f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 3 Jan 2023 14:26:57 +0100 Subject: [PATCH 563/758] windows.media.speech: Allow the recognition session worker to be paused. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit c8a68f2a7a7d458bdf27913eb52f5544a618b0ec) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 41 +++++++++++++++++++++--- dlls/windows.media.speech/tests/speech.c | 4 +-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index aabfd56694a..80cf40fbef9 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -162,7 +162,7 @@ struct session struct list result_handlers; HANDLE worker_thread, worker_control_event; - BOOLEAN worker_running; + BOOLEAN worker_running, worker_paused; CRITICAL_SECTION cs; }; @@ -362,6 +362,7 @@ static HRESULT WINAPI session_StopAsync( ISpeechContinuousRecognitionSession *if thread = impl->worker_thread; impl->worker_thread = INVALID_HANDLE_VALUE; impl->worker_running = FALSE; + impl->worker_paused = FALSE; } else { @@ -401,14 +402,44 @@ static HRESULT session_pause_async( IInspectable *invoker ) static HRESULT WINAPI session_PauseAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { - FIXME("iface %p, action %p stub!\n", iface, action); - return async_action_create(NULL, session_pause_async, action); + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + HRESULT hr = S_OK; + + TRACE("iface %p, action %p.\n", iface, action); + + *action = NULL; + + if (FAILED(hr = async_action_create(NULL, session_pause_async, action))) + return hr; + + EnterCriticalSection(&impl->cs); + if (impl->worker_running) + { + impl->worker_paused = TRUE; + } + LeaveCriticalSection(&impl->cs); + + SetEvent(impl->worker_control_event); + + return hr; } static HRESULT WINAPI session_Resume( ISpeechContinuousRecognitionSession *iface ) { - FIXME("iface %p stub!\n", iface); - return E_NOTIMPL; + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + + TRACE("iface %p.\n", iface); + + EnterCriticalSection(&impl->cs); + if (impl->worker_running) + { + impl->worker_paused = FALSE; + } + LeaveCriticalSection(&impl->cs); + + SetEvent(impl->worker_control_event); + + return S_OK; } static HRESULT WINAPI session_add_Completed( ISpeechContinuousRecognitionSession *iface, diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index c358aa70927..8037b0e8354 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1798,11 +1798,11 @@ static void test_Recognition(void) IAsyncAction_Release(action2); hr = ISpeechContinuousRecognitionSession_Resume(session); - todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); /* Resume when already resumed. */ hr = ISpeechContinuousRecognitionSession_Resume(session); - todo_wine ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); + ok(hr == S_OK, "ISpeechContinuousRecognitionSession_Resume failed, hr %#lx.\n", hr); recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); From 9bca8bd636cd6399d02a391dbd3899c109b3b6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Fri, 6 Jan 2023 18:19:31 +0100 Subject: [PATCH 564/758] windows.media.speech: Add an audio capturing system. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 85ce23a9bf8ac6501e042590448e710012c15987) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 124 ++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 80cf40fbef9..0def2fc3cbd 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -19,6 +19,10 @@ #include "private.h" +#include "initguid.h" +#include "audioclient.h" +#include "mmdeviceapi.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(speech); @@ -161,7 +165,11 @@ struct session struct list completed_handlers; struct list result_handlers; - HANDLE worker_thread, worker_control_event; + IAudioClient *audio_client; + IAudioCaptureClient *capture_client; + WAVEFORMATEX capture_wfx; + + HANDLE worker_thread, worker_control_event, audio_buf_event; BOOLEAN worker_running, worker_paused; CRITICAL_SECTION cs; }; @@ -181,25 +189,73 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); - BOOLEAN running = TRUE; - DWORD status; + BOOLEAN running = TRUE, paused = FALSE; + DWORD flags, status; + HANDLE events[2]; + BYTE *audio_buf; + HRESULT hr; SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker"); + if (FAILED(hr = IAudioClient_Start(impl->audio_client))) + goto error; + while (running) { - status = WaitForMultipleObjects(1, &impl->worker_control_event, FALSE, INFINITE); + BOOLEAN old_paused = paused; + UINT32 count = 0; + + events[count++] = impl->worker_control_event; + if (!paused) events[count++] = impl->audio_buf_event; + + status = WaitForMultipleObjects(count, events, FALSE, INFINITE); if (status == 0) /* worker_control_event signaled */ { EnterCriticalSection(&impl->cs); + paused = impl->worker_paused; running = impl->worker_running; LeaveCriticalSection(&impl->cs); + + if (old_paused < paused) + { + if (FAILED(hr = IAudioClient_Stop(impl->audio_client))) goto error; + if (FAILED(hr = IAudioClient_Reset(impl->audio_client))) goto error; + TRACE("session worker paused.\n"); + } + else if (old_paused > paused) + { + if (FAILED(hr = IAudioClient_Start(impl->audio_client))) goto error; + TRACE("session worker resumed.\n"); + } } + else if (status == 1) /* audio_buf_event signaled */ + { + UINT32 frames_available = 0; - /* TODO: Send mic data to recognizer and handle results. */ + while (IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK) + { + /* TODO: Send mic data to recognizer and handle results. */ + IAudioCaptureClient_ReleaseBuffer(impl->capture_client, frames_available); + } + } + else + { + ERR("Unexpected state entered. Aborting worker.\n"); + break; + } } + if (FAILED(hr = IAudioClient_Stop(impl->audio_client))) + ERR("IAudioClient_Stop failed with %#lx.\n", hr); + + if (FAILED(hr = IAudioClient_Reset(impl->audio_client))) + ERR("IAudioClient_Reset failed with %#lx.\n", hr); + return 0; + +error: + ERR("The recognition session worker encountered a serious error and needs to stop. hr: %lx.\n", hr); + return 1; } static HRESULT WINAPI session_QueryInterface( ISpeechContinuousRecognitionSession *iface, REFIID iid, void **out ) @@ -252,6 +308,9 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface typed_event_handlers_clear(&impl->completed_handlers); typed_event_handlers_clear(&impl->result_handlers); + IAudioCaptureClient_Release(impl->capture_client); + IAudioClient_Release(impl->audio_client); + impl->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&impl->cs); @@ -926,6 +985,55 @@ static const struct IActivationFactoryVtbl activation_factory_vtbl = DEFINE_IINSPECTABLE(recognizer_factory, ISpeechRecognizerFactory, struct recognizer_statics, IActivationFactory_iface) +static HRESULT recognizer_factory_create_audio_capture(struct session *session) +{ + const REFERENCE_TIME buffer_duration = 5000000; /* 0.5 second */ + IMMDeviceEnumerator *mm_enum = NULL; + IMMDevice *mm_device = NULL; + WAVEFORMATEX wfx = { 0 }; + WCHAR *str = NULL; + HRESULT hr = S_OK; + + if (!(session->audio_buf_event = CreateEventW(NULL, FALSE, FALSE, NULL))) + return HRESULT_FROM_WIN32(GetLastError()); + + if (FAILED(hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void **)&mm_enum))) + goto cleanup; + + if (FAILED(hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(mm_enum, eCapture, eMultimedia, &mm_device))) + goto cleanup; + + if (FAILED(hr = IMMDevice_Activate(mm_device, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&session->audio_client))) + goto cleanup; + + hr = IMMDevice_GetId(mm_device, &str); + TRACE("selected capture device ID: %s, hr %#lx\n", debugstr_w(str), hr); + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = 16000; + wfx.nChannels = 1; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = (wfx.wBitsPerSample + 7) / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + TRACE("wfx tag %u, channels %u, samples %lu, bits %u, align %u.\n", wfx.wFormatTag, wfx.nChannels, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nBlockAlign); + + if (FAILED(hr = IAudioClient_Initialize(session->audio_client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffer_duration, 0, &wfx, NULL))) + goto cleanup; + + if (FAILED(hr = IAudioClient_SetEventHandle(session->audio_client, session->audio_buf_event))) + goto cleanup; + + hr = IAudioClient_GetService(session->audio_client, &IID_IAudioCaptureClient, (void **)&session->capture_client); + + session->capture_wfx = wfx; + +cleanup: + if (mm_device) IMMDevice_Release(mm_device); + if (mm_enum) IMMDeviceEnumerator_Release(mm_enum); + CoTaskMemFree(str); + return hr; +} + static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -969,6 +1077,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&session->constraints))) goto error; + if (FAILED(hr = recognizer_factory_create_audio_capture(session))) + goto error; + InitializeCriticalSection(&session->cs); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); @@ -984,6 +1095,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface return S_OK; error: + if (session->capture_client) IAudioCaptureClient_Release(session->capture_client); + if (session->audio_client) IAudioClient_Release(session->audio_client); + if (session->audio_buf_event) CloseHandle(session->audio_buf_event); if (session->constraints) IVector_ISpeechRecognitionConstraint_Release(session->constraints); if (session->worker_control_event) CloseHandle(session->worker_control_event); free(session); From a82d0ebba7462de70162d6df32ab7ecb9a0e8060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Sun, 15 Jan 2023 13:15:17 +0100 Subject: [PATCH 565/758] windows.media.speech: Partially implement the speech recognizer state. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 74129bd8fb619806b1c4fcb09c0db2fedca72efd) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 21 +++++++++++-- dlls/windows.media.speech/tests/speech.c | 40 ++++++++++++------------ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 0def2fc3cbd..72c08a1b06c 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -162,6 +162,8 @@ struct session IVector_ISpeechRecognitionConstraint *constraints; + SpeechRecognizerState recognizer_state; + struct list completed_handlers; struct list result_handlers; @@ -379,6 +381,7 @@ static HRESULT WINAPI session_StartAsync( ISpeechContinuousRecognitionSession *i else { impl->worker_running = TRUE; + impl->recognizer_state = SpeechRecognizerState_Capturing; } LeaveCriticalSection(&impl->cs); @@ -422,6 +425,7 @@ static HRESULT WINAPI session_StopAsync( ISpeechContinuousRecognitionSession *if impl->worker_thread = INVALID_HANDLE_VALUE; impl->worker_running = FALSE; impl->worker_paused = FALSE; + impl->recognizer_state = SpeechRecognizerState_Idle; } else { @@ -475,6 +479,7 @@ static HRESULT WINAPI session_PauseAsync( ISpeechContinuousRecognitionSession *i if (impl->worker_running) { impl->worker_paused = TRUE; + impl->recognizer_state = SpeechRecognizerState_Paused; } LeaveCriticalSection(&impl->cs); @@ -493,6 +498,7 @@ static HRESULT WINAPI session_Resume( ISpeechContinuousRecognitionSession *iface if (impl->worker_running) { impl->worker_paused = FALSE; + impl->recognizer_state = SpeechRecognizerState_Capturing; } LeaveCriticalSection(&impl->cs); @@ -816,8 +822,19 @@ static HRESULT WINAPI recognizer2_get_ContinuousRecognitionSession( ISpeechRecog static HRESULT WINAPI recognizer2_get_State( ISpeechRecognizer2 *iface, SpeechRecognizerState *state ) { - FIXME("iface %p, state %p stub!\n", iface, state); - return E_NOTIMPL; + struct recognizer *impl = impl_from_ISpeechRecognizer2(iface); + struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + + FIXME("iface %p, state %p not all states are supported, yet.\n", iface, state); + + if (!state) + return E_POINTER; + + EnterCriticalSection(&session->cs); + *state = session->recognizer_state; + LeaveCriticalSection(&session->cs); + + return S_OK; } static HRESULT WINAPI recognizer2_StopRecognitionAsync( ISpeechRecognizer2 *iface, IAsyncAction **action ) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 8037b0e8354..6bc5a8b1751 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1721,8 +1721,8 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); hr = ISpeechRecognizer_CompileConstraintsAsync(recognizer, &operation); ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr); @@ -1771,8 +1771,8 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); /* * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. @@ -1786,9 +1786,9 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Paused || - broken(recog_state == SpeechRecognizerState_Capturing) /* Broken on Win10 1507 */, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Paused || + broken(recog_state == SpeechRecognizerState_Capturing) /* Broken on Win10 1507 */, "recog_state was %u.\n", recog_state); /* Check what happens if we try to pause again, when the session is already paused. */ hr = ISpeechContinuousRecognitionSession_PauseAsync(session, &action2); @@ -1806,8 +1806,8 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action2); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); @@ -1858,8 +1858,8 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); /* Try stopping, when already stopped. */ hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); @@ -1879,9 +1879,9 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Paused || - broken(recog_state == SpeechRecognizerState_Capturing) /* Broken on Win10 1507 */, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Paused || + broken(recog_state == SpeechRecognizerState_Capturing) /* Broken on Win10 1507 */, "recog_state was %u.\n", recog_state); hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); @@ -1890,8 +1890,8 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Idle, "recog_state was %u.\n", recog_state); hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); @@ -1900,10 +1900,10 @@ static void test_Recognition(void) recog_state = 0xdeadbeef; hr = ISpeechRecognizer2_get_State(recognizer2, &recog_state); - todo_wine ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); - todo_wine ok(recog_state == SpeechRecognizerState_Capturing - || broken(recog_state == SpeechRecognizerState_Idle) /* Sometimes Windows is a little behind. */, - "recog_state was %u.\n", recog_state); + ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); + ok(recog_state == SpeechRecognizerState_Capturing + || broken(recog_state == SpeechRecognizerState_Idle) /* Sometimes Windows is a little behind. */, + "recog_state was %u.\n", recog_state); hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_PauseAsync failed, hr %#lx.\n", hr); From 3c01cf3f84182c077dec0891538b64c1cfd1f6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Wed, 18 May 2022 18:47:30 +0200 Subject: [PATCH 566/758] windows.media.speech: Store recorded audio in a temporary ringbuffer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit d1d186eef20f4960b32f1f0d1b7df41726b43792) CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 34 +++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 72c08a1b06c..c2f386206b8 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -192,9 +192,10 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); BOOLEAN running = TRUE, paused = FALSE; + UINT32 frame_count, tmp_buf_size; + BYTE *audio_buf, *tmp_buf; DWORD flags, status; HANDLE events[2]; - BYTE *audio_buf; HRESULT hr; SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker"); @@ -202,6 +203,16 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) if (FAILED(hr = IAudioClient_Start(impl->audio_client))) goto error; + if (FAILED(hr = IAudioClient_GetBufferSize(impl->audio_client, &frame_count))) + goto error; + + tmp_buf_size = sizeof(*tmp_buf) * frame_count * impl->capture_wfx.nBlockAlign; + if (!(tmp_buf = malloc(tmp_buf_size))) + { + ERR("Memory allocation failed.\n"); + return 1; + } + while (running) { BOOLEAN old_paused = paused; @@ -232,13 +243,28 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) } else if (status == 1) /* audio_buf_event signaled */ { + SIZE_T packet_size = 0, tmp_buf_offset = 0; UINT32 frames_available = 0; - while (IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK) + while (tmp_buf_offset < tmp_buf_size + && IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK) { - /* TODO: Send mic data to recognizer and handle results. */ + packet_size = frames_available * impl->capture_wfx.nBlockAlign; + if (tmp_buf_offset + packet_size > tmp_buf_size) + { + /* Defer processing until the next iteration of the worker loop. */ + IAudioCaptureClient_ReleaseBuffer(impl->capture_client, 0); + SetEvent(impl->audio_buf_event); + break; + } + + memcpy(tmp_buf + tmp_buf_offset, audio_buf, packet_size); + tmp_buf_offset += packet_size; + IAudioCaptureClient_ReleaseBuffer(impl->capture_client, frames_available); } + + /* TODO: Send mic data to recognizer and handle results. */ } else { @@ -253,6 +279,8 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) if (FAILED(hr = IAudioClient_Reset(impl->audio_client))) ERR("IAudioClient_Reset failed with %#lx.\n", hr); + free(tmp_buf); + return 0; error: From 601a4b41d09ffac840c8514b7dbbe8f9b6633fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Feb 2023 15:05:53 +0100 Subject: [PATCH 567/758] windows.media.speech: Add Vosk checks to autoconf. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- configure.ac | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 14c80983a63..9e9c9ada9bb 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,7 @@ AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev],[do not use udev (plug an AC_ARG_WITH(unwind, AS_HELP_STRING([--without-unwind],[do not use the libunwind library (exception handling)])) AC_ARG_WITH(usb, AS_HELP_STRING([--without-usb],[do not use the libusb library])) AC_ARG_WITH(v4l2, AS_HELP_STRING([--without-v4l2],[do not use v4l2 (video capture)])) +AC_ARG_WITH(vosk, AS_HELP_STRING([--without-vosk],[do not use Vosk])) AC_ARG_WITH(vulkan, AS_HELP_STRING([--without-vulkan],[do not use Vulkan])) AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]), [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xcomposite_h=no; fi]) @@ -487,7 +488,8 @@ AC_CHECK_HEADERS(\ syscall.h \ utime.h \ valgrind/memcheck.h \ - valgrind/valgrind.h + valgrind/valgrind.h \ + vosk_api.h ) WINE_HEADER_MAJOR() AC_HEADER_STAT() @@ -1792,6 +1794,14 @@ then WINE_WARNING([No sound system was found. Windows applications will be silent.]) fi +dnl **** Check for Vosk **** +if test x$with_vosk != xno +then + WINE_CHECK_SONAME(vosk,vosk_recognizer_new) +fi +WINE_NOTICE_WITH(vosk,[test x$ac_cv_lib_soname_vosk = x], + [libvosk ${notice_platform}development files not found, speech recognition won't be supported.]) + dnl *** Check for Vulkan *** if test "x$with_vulkan" != "xno" then From be0a0730f7c9a469ac3994c699fbc67958a6d710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Feb 2023 00:17:50 +0100 Subject: [PATCH 568/758] windows.media.speech: Add unixlib stub. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 + dlls/windows.media.speech/main.c | 26 ++++++ dlls/windows.media.speech/private.h | 4 + dlls/windows.media.speech/unixlib.c | 122 ++++++++++++++++++++++++++ dlls/windows.media.speech/unixlib.h | 39 ++++++++ 5 files changed, 193 insertions(+) create mode 100644 dlls/windows.media.speech/unixlib.c create mode 100644 dlls/windows.media.speech/unixlib.h diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 10903cb1d7b..64376514d58 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,4 +1,5 @@ MODULE = windows.media.speech.dll +UNIXLIB = windows.media.speech.so IMPORTS = combase uuid C_SRCS = \ @@ -8,6 +9,7 @@ C_SRCS = \ main.c \ recognizer.c \ synthesizer.c \ + unixlib.c \ vector.c IDL_SRCS = classes.idl diff --git a/dlls/windows.media.speech/main.c b/dlls/windows.media.speech/main.c index e772a791588..d53e1599eb8 100644 --- a/dlls/windows.media.speech/main.c +++ b/dlls/windows.media.speech/main.c @@ -20,10 +20,36 @@ #include "initguid.h" #include "private.h" +#include "unixlib.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(speech); +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) +{ + NTSTATUS status; + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(instance); + + if ((status = __wine_init_unix_call())) + ERR("loading the unixlib failed with status %#lx.\n", status); + + if ((status = WINE_UNIX_CALL(unix_process_attach, NULL))) + WARN("initializing the unixlib failed with status %#lx.\n", status); + + break; + case DLL_PROCESS_DETACH: + WINE_UNIX_CALL(unix_process_detach, NULL); + break; + } + + return TRUE; +} + HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) { FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index e80d73ec1fb..2f804fbf1a7 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -22,6 +22,10 @@ #include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" #define COBJMACROS #include "corerror.h" #include "windef.h" diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c new file mode 100644 index 00000000000..d6f748b9426 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.c @@ -0,0 +1,122 @@ +/* + * Unixlib for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef SONAME_LIBVOSK +#include +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" + +#include "wine/debug.h" + +#include "unixlib.h" + +WINE_DEFAULT_DEBUG_CHANNEL(speech); +#ifdef SONAME_LIBVOSK +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +static void *vosk_handle; +#define MAKE_FUNCPTR( f ) static typeof(f) * p_##f +MAKE_FUNCPTR(vosk_model_new); +MAKE_FUNCPTR(vosk_model_free); +MAKE_FUNCPTR(vosk_recognizer_new); +MAKE_FUNCPTR(vosk_recognizer_free); +#undef MAKE_FUNCPTR + +static NTSTATUS process_attach( void *args ) +{ + if (!(vosk_handle = dlopen(SONAME_LIBVOSK, RTLD_NOW))) + { + ERR_(winediag)("Wine is unable to load the Unix side dependencies for speech recognition. " + "Make sure Vosk is installed and up to date on your system and try again.\n"); + return STATUS_DLL_NOT_FOUND; + } + +#define LOAD_FUNCPTR( f ) \ + if (!(p_##f = dlsym(vosk_handle, #f))) \ + { \ + ERR("failed to load %s\n", #f); \ + goto error; \ + } + LOAD_FUNCPTR(vosk_model_new) + LOAD_FUNCPTR(vosk_model_free) + LOAD_FUNCPTR(vosk_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_free) +#undef LOAD_FUNCPTR + + return STATUS_SUCCESS; + +error: + dlclose(vosk_handle); + vosk_handle = NULL; + return STATUS_DLL_NOT_FOUND; +} + +static NTSTATUS process_detach( void *args ) +{ + if (vosk_handle) + { + dlclose(vosk_handle); + vosk_handle = NULL; + } + return STATUS_SUCCESS; +} + +#else /* SONAME_LIBVOSK */ + +#define MAKE_UNSUPPORTED_FUNC( f ) \ + static NTSTATUS f( void *args ) \ + { \ + ERR("wine was compiled without Vosk support. Speech recognition won't work.\n"); \ + return STATUS_NOT_SUPPORTED; \ + } + +MAKE_UNSUPPORTED_FUNC(process_attach) +MAKE_UNSUPPORTED_FUNC(process_detach) +#undef MAKE_UNSUPPORTED_FUNC + +#endif /* SONAME_LIBVOSK */ + +unixlib_entry_t __wine_unix_call_funcs[] = +{ + process_attach, + process_detach, +}; + +unixlib_entry_t __wine_unix_call_wow64_funcs[] = +{ + process_attach, + process_detach, +}; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h new file mode 100644 index 00000000000..6c337e54511 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.h @@ -0,0 +1,39 @@ +/* + * Unix library interface for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl 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 + */ + +#ifndef __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H +#define __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H + +#include +#include + +#include "windef.h" +#include "winternl.h" +#include "wtypes.h" + +#include "wine/unixlib.h" + +enum unix_funcs +{ + unix_process_attach, + unix_process_detach, +}; + +#endif From 3b281dd956ded1a0963944f5147a7c2bf72537e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 6 Feb 2023 21:24:26 +0100 Subject: [PATCH 569/758] windows.media.speech/tests: Get rid of duplicated hresult. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 6bc5a8b1751..c7c7b6eb040 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -42,7 +42,6 @@ #define AsyncStatus_Closed 4 #define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 -#define SPERR_WINRT_INCORRECT_FORMAT 0x80131537 #define IHandler_RecognitionResult ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs #define IHandler_RecognitionResultVtbl ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgsVtbl @@ -1005,7 +1004,7 @@ static void test_SpeechSynthesizer(void) operation_ss_stream = (void *)0xdeadbeef; hr = ISpeechSynthesizer_SynthesizeSsmlToStreamAsync(synthesizer, str, &operation_ss_stream); /* Broken on Win 8 + 8.1 */ - ok(hr == S_OK || broken(hr == SPERR_WINRT_INCORRECT_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); + ok(hr == S_OK || broken(hr == COR_E_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); if (hr == S_OK) { From 413a02eb1179ab35af8fddd8a7f8ac53120151e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 20 Feb 2023 23:10:16 +0100 Subject: [PATCH 570/758] windows.media.speech/tests: Allow the SpeechRecognizer creation to fail in Wine. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow for error handling of missing Unix-side dependencies. Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index c7c7b6eb040..57e216b78d5 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1192,7 +1192,7 @@ static void test_SpeechRecognizer(void) ok(ref == 1, "Got unexpected ref %lu.\n", ref); hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR, "Got unexpected hr %#lx.\n", hr); if (hr == S_OK) { @@ -1411,7 +1411,7 @@ static void test_SpeechRecognizer(void) } else if (hr == SPERR_WINRT_INTERNAL_ERROR) /* Not sure when this triggers. Probably if a language pack is not installed. */ { - win_skip("Could not init SpeechRecognizer with default language!\n"); + skip("Could not init SpeechRecognizer with default language!\n"); } done: @@ -1651,12 +1651,12 @@ static void test_Recognition(void) ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR || hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR || broken(hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); WindowsDeleteString(hstr); - if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. */ + if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. Wine with missing Unix side dependencies. */ { - win_skip("SpeechRecognizer cannot be activated!\n"); + skip("SpeechRecognizer cannot be activated!\n"); goto done; } From a84579d413d0ee7648370756cf209f779e7545b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 20 Feb 2023 23:11:02 +0100 Subject: [PATCH 571/758] windows.media.speech: Implement Vosk create and release functions in the unixlib. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 +- dlls/windows.media.speech/private.h | 3 + dlls/windows.media.speech/recognizer.c | 42 ++++++ dlls/windows.media.speech/unixlib.c | 169 ++++++++++++++++++++++++- dlls/windows.media.speech/unixlib.h | 16 +++ 5 files changed, 230 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 64376514d58..455f81c0840 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,6 +1,6 @@ MODULE = windows.media.speech.dll UNIXLIB = windows.media.speech.so -IMPORTS = combase uuid +IMPORTS = combase uuid user32 C_SRCS = \ async.c \ diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 2f804fbf1a7..62952478bdf 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -31,6 +31,7 @@ #include "windef.h" #include "winbase.h" #include "winstring.h" +#include "winuser.h" #include "objbase.h" #include "activation.h" @@ -47,6 +48,8 @@ #include "wine/list.h" +#define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 + /* * * Windows.Media.SpeechRecognition diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index c2f386206b8..6938f6d7604 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -25,6 +25,9 @@ #include "wine/debug.h" +#include "unixlib.h" +#include "wine/unixlib.h" + WINE_DEFAULT_DEBUG_CHANNEL(speech); /* @@ -171,6 +174,8 @@ struct session IAudioCaptureClient *capture_client; WAVEFORMATEX capture_wfx; + speech_recognizer_handle unix_handle; + HANDLE worker_thread, worker_control_event, audio_buf_event; BOOLEAN worker_running, worker_paused; CRITICAL_SECTION cs; @@ -318,7 +323,9 @@ static ULONG WINAPI session_AddRef( ISpeechContinuousRecognitionSession *iface ) static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct speech_release_recognizer_params release_params; ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) @@ -344,6 +351,9 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface impl->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&impl->cs); + release_params.handle = impl->unix_handle; + WINE_UNIX_CALL(unix_speech_release_recognizer, &release_params); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -1079,6 +1089,35 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) return hr; } +static HRESULT recognizer_factory_create_unix_instance( struct session *session ) +{ + struct speech_create_recognizer_params create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->unix_handle = create_params.handle; + + return S_OK; +} + static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1125,6 +1164,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error; + if (FAILED(hr = recognizer_factory_create_unix_instance(session))) + goto error; + InitializeCriticalSection(&session->cs); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index d6f748b9426..362649e76f2 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -26,9 +26,12 @@ #include #include -#include #include +#include +#include #include +#include +#include #ifdef SONAME_LIBVOSK #include @@ -94,6 +97,164 @@ static NTSTATUS process_detach( void *args ) return STATUS_SUCCESS; } +static inline speech_recognizer_handle vosk_recognizer_to_handle( VoskRecognizer *recognizer ) +{ + return (speech_recognizer_handle)(UINT_PTR)recognizer; +} + +static inline VoskRecognizer *vosk_recognizer_from_handle( speech_recognizer_handle handle ) +{ + return (VoskRecognizer *)(UINT_PTR)handle; +} + +static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) +{ + static const char *vosk_model_identifier_small = "vosk-model-small-"; + static const char *vosk_model_identifier = "vosk-model-"; + size_t ident_small_len = strlen(vosk_model_identifier_small); + size_t ident_len = strlen(vosk_model_identifier); + char *ent_name, *model_path, *best_match, *delim; + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct dirent *dirent; + size_t path_len, len; + DIR *dir; + + TRACE("path %s, locale %s, model %p.\n", path, debugstr_a(locale), model); + + if (!path || !locale || (len = strlen(locale)) < 4) + return STATUS_UNSUCCESSFUL; + + if (!(dir = opendir(path))) + return STATUS_UNSUCCESSFUL; + + delim = strchr(locale, '-'); + path_len = strlen(path); + best_match = NULL; + *model = NULL; + + while ((dirent = readdir(dir))) + { + ent_name = dirent->d_name; + + if (!strncmp(ent_name, vosk_model_identifier_small, ident_small_len)) + ent_name += ident_small_len; + else if (!strncmp(ent_name, vosk_model_identifier, ident_len)) + ent_name += ident_len; + else + continue; + + /* + * Find the first matching model for lang and region (en-us). + * If there isn't any, pick the first one just matching lang (en). + */ + if (!strncmp(ent_name, locale, len)) + { + if (best_match) free(best_match); + best_match = strdup(dirent->d_name); + break; + } + + if (!best_match && !strncmp(ent_name, locale, delim - locale)) + best_match = strdup(dirent->d_name); + } + + closedir(dir); + + if (!best_match) + return STATUS_UNSUCCESSFUL; + + if (!(model_path = malloc(path_len + 1 /* '/' */ + strlen(best_match) + 1))) + { + status = STATUS_NO_MEMORY; + goto done; + } + + sprintf(model_path, "%s/%s", path, best_match); + + TRACE("trying to load Vosk model %s.\n", debugstr_a(model_path)); + + if ((*model = p_vosk_model_new(model_path)) != NULL) + status = STATUS_SUCCESS; + +done: + free(model_path); + free(best_match); + + return status; +} + +static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) +{ + const char *suffix = NULL; + char *env, *path = NULL; + NTSTATUS status; + + TRACE("locale %s, model %p.\n", debugstr_a(locale), model); + + if (!model) + return STATUS_UNSUCCESSFUL; + + if (!find_model_by_locale_and_path(getenv("VOSK_MODEL_PATH"), locale, model)) + return STATUS_SUCCESS; + if (!find_model_by_locale_and_path("/usr/share/vosk", locale, model)) + return STATUS_SUCCESS; + + if ((env = getenv("XDG_CACHE_HOME"))) + suffix = "/vosk"; + else if ((env = getenv("HOME"))) + suffix = "/.cache/vosk"; + else + return STATUS_UNSUCCESSFUL; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + + return status; +} + +static NTSTATUS speech_create_recognizer( void *args ) +{ + struct speech_create_recognizer_params *params = args; + VoskRecognizer *recognizer = NULL; + VoskModel *model = NULL; + NTSTATUS status = STATUS_SUCCESS; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if ((status = find_model_by_locale(params->locale, &model))) + return status; + + if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + + /* VoskModel is reference-counted. A VoskRecognizer keeps a reference to its model. */ + p_vosk_model_free(model); + + params->handle = vosk_recognizer_to_handle(recognizer); + return status; +} + +static NTSTATUS speech_release_recognizer( void *args ) +{ + struct speech_release_recognizer_params *params = args; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + p_vosk_recognizer_free(vosk_recognizer_from_handle(params->handle)); + + return STATUS_SUCCESS; +} + #else /* SONAME_LIBVOSK */ #define MAKE_UNSUPPORTED_FUNC( f ) \ @@ -105,6 +266,8 @@ static NTSTATUS process_detach( void *args ) MAKE_UNSUPPORTED_FUNC(process_attach) MAKE_UNSUPPORTED_FUNC(process_detach) +MAKE_UNSUPPORTED_FUNC(speech_create_recognizer) +MAKE_UNSUPPORTED_FUNC(speech_release_recognizer) #undef MAKE_UNSUPPORTED_FUNC #endif /* SONAME_LIBVOSK */ @@ -113,10 +276,14 @@ unixlib_entry_t __wine_unix_call_funcs[] = { process_attach, process_detach, + speech_create_recognizer, + speech_release_recognizer, }; unixlib_entry_t __wine_unix_call_wow64_funcs[] = { process_attach, process_detach, + speech_create_recognizer, + speech_release_recognizer, }; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 6c337e54511..974e8d5f797 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -30,10 +30,26 @@ #include "wine/unixlib.h" +typedef UINT64 speech_recognizer_handle; + +struct speech_create_recognizer_params +{ + speech_recognizer_handle handle; + CHAR locale[LOCALE_NAME_MAX_LENGTH]; + FLOAT sample_rate; +}; + +struct speech_release_recognizer_params +{ + speech_recognizer_handle handle; +}; + enum unix_funcs { unix_process_attach, unix_process_detach, + unix_speech_create_recognizer, + unix_speech_release_recognizer, }; #endif From bb2aa03f422221b5944dbef7dd22d8dcb6b2f2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 6 Mar 2023 23:51:31 +0100 Subject: [PATCH 572/758] windows.media.speech: Move unix side recognizer creation to recognizer_CompileConstraintsAsync(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 76 ++++++++++++++------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 6938f6d7604..81f2b9d3155 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -734,17 +734,55 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } +static HRESULT recognizer_create_unix_instance( struct session *session ) +{ + struct speech_create_recognizer_params create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->unix_handle = create_params.handle; + + return S_OK; +} + static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IInspectable **result ) { - return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); + struct recognizer *impl = impl_from_ISpeechRecognizer((ISpeechRecognizer *)invoker); + struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + HRESULT hr; + + if (FAILED(hr = recognizer_create_unix_instance(session))) + { + WARN("Failed to created recognizer instance.\n"); + return compilation_result_create(SpeechRecognitionResultStatus_GrammarCompilationFailure, (ISpeechRecognitionCompilationResult **) result); + } + else return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); } static HRESULT WINAPI recognizer_CompileConstraintsAsync( ISpeechRecognizer *iface, IAsyncOperation_SpeechRecognitionCompilationResult **operation ) { IAsyncOperation_IInspectable **value = (IAsyncOperation_IInspectable **)operation; - FIXME("iface %p, operation %p semi-stub!\n", iface, operation); - return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, recognizer_compile_constraints_async, value); + TRACE("iface %p, operation %p semi-stub!\n", iface, operation); + return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, (IInspectable *)iface, recognizer_compile_constraints_async, value); } static HRESULT WINAPI recognizer_RecognizeAsync( ISpeechRecognizer *iface, @@ -1089,35 +1127,6 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) return hr; } -static HRESULT recognizer_factory_create_unix_instance( struct session *session ) -{ - struct speech_create_recognizer_params create_params = { 0 }; - WCHAR locale[LOCALE_NAME_MAX_LENGTH]; - NTSTATUS status; - INT len; - - if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) - return E_FAIL; - - if (CharLowerBuffW(locale, len) != len) - return E_FAIL; - - if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) - return HRESULT_FROM_WIN32(GetLastError()); - - create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; - - if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) - { - ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); - return SPERR_WINRT_INTERNAL_ERROR; - } - - session->unix_handle = create_params.handle; - - return S_OK; -} - static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1164,9 +1173,6 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error; - if (FAILED(hr = recognizer_factory_create_unix_instance(session))) - goto error; - InitializeCriticalSection(&session->cs); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); From 18dde847c6648547f86869a9fc5b2c1d83748ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 27 Feb 2023 13:22:00 +0100 Subject: [PATCH 573/758] windows.media.speech: Implement recognition in the unixlib. CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 35 +++++++++++- dlls/windows.media.speech/unixlib.c | 79 ++++++++++++++++++++++++++ dlls/windows.media.speech/unixlib.h | 26 ++++++++- 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 81f2b9d3155..eb5fd9277a4 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -196,10 +196,13 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct speech_get_recognition_result_params recognition_result_params; + struct speech_recognize_audio_params recognize_audio_params; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf; DWORD flags, status; + NTSTATUS nt_status; HANDLE events[2]; HRESULT hr; @@ -269,7 +272,37 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) IAudioCaptureClient_ReleaseBuffer(impl->capture_client, frames_available); } - /* TODO: Send mic data to recognizer and handle results. */ + recognize_audio_params.handle = impl->unix_handle; + recognize_audio_params.samples = tmp_buf; + recognize_audio_params.samples_size = tmp_buf_offset; + recognize_audio_params.status = RECOGNITION_STATUS_EXCEPTION; + + if (NT_ERROR(nt_status = WINE_UNIX_CALL(unix_speech_recognize_audio, &recognize_audio_params))) + WARN("unix_speech_recognize_audio failed with status %#lx.\n", nt_status); + + if (recognize_audio_params.status != RECOGNITION_STATUS_RESULT_AVAILABLE) + continue; + + recognition_result_params.handle = impl->unix_handle; + recognition_result_params.result_buf = NULL; + recognition_result_params.result_buf_size = 512; + + do + { + recognition_result_params.result_buf = realloc(recognition_result_params.result_buf, recognition_result_params.result_buf_size); + } + while (WINE_UNIX_CALL(unix_speech_get_recognition_result, &recognition_result_params) == STATUS_BUFFER_TOO_SMALL && + recognition_result_params.result_buf); + + if (!recognition_result_params.result_buf) + { + WARN("memory allocation failed.\n"); + break; + } + + /* TODO: Compare recognized text to available options. */ + + free(recognition_result_params.result_buf); } else { diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 362649e76f2..177256b2060 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -56,6 +56,9 @@ MAKE_FUNCPTR(vosk_model_new); MAKE_FUNCPTR(vosk_model_free); MAKE_FUNCPTR(vosk_recognizer_new); MAKE_FUNCPTR(vosk_recognizer_free); +MAKE_FUNCPTR(vosk_recognizer_accept_waveform); +MAKE_FUNCPTR(vosk_recognizer_final_result); +MAKE_FUNCPTR(vosk_recognizer_reset); #undef MAKE_FUNCPTR static NTSTATUS process_attach( void *args ) @@ -77,6 +80,9 @@ static NTSTATUS process_attach( void *args ) LOAD_FUNCPTR(vosk_model_free) LOAD_FUNCPTR(vosk_recognizer_new) LOAD_FUNCPTR(vosk_recognizer_free) + LOAD_FUNCPTR(vosk_recognizer_accept_waveform) + LOAD_FUNCPTR(vosk_recognizer_final_result) + LOAD_FUNCPTR(vosk_recognizer_reset) #undef LOAD_FUNCPTR return STATUS_SUCCESS; @@ -255,6 +261,73 @@ static NTSTATUS speech_release_recognizer( void *args ) return STATUS_SUCCESS; } +static NTSTATUS speech_recognize_audio( void *args ) +{ + struct speech_recognize_audio_params *params = args; + VoskRecognizer *recognizer = vosk_recognizer_from_handle(params->handle); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if (!recognizer) + return STATUS_UNSUCCESSFUL; + + params->status = p_vosk_recognizer_accept_waveform(recognizer, (const char *)params->samples, params->samples_size); + + return STATUS_SUCCESS; +} + +static NTSTATUS speech_get_recognition_result( void* args ) +{ + struct speech_get_recognition_result_params *params = args; + VoskRecognizer *recognizer = vosk_recognizer_from_handle(params->handle); + static const char *result_json_start = "{\n \"text\" : \""; + const size_t json_start_len = strlen(result_json_start); + static size_t last_result_len = 0; + static char *last_result = NULL; + const char *tmp = NULL; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if (!recognizer) + return STATUS_UNSUCCESSFUL; + + if (!last_result) + { + if ((tmp = p_vosk_recognizer_final_result(recognizer))) + { + last_result = strdup(tmp); + tmp = last_result; + + /* Operations to remove the JSON wrapper "{\n \"text\" : \"some recognized text\"\n}" -> "some recognized text\0" */ + memmove(last_result, last_result + json_start_len, strlen(last_result) - json_start_len + 1); + last_result = strrchr(last_result, '\"'); + last_result[0] = '\0'; + + last_result = (char *)tmp; + last_result_len = strlen(last_result); + TRACE("last_result %s.\n", debugstr_a(last_result)); + } + else return STATUS_NOT_FOUND; + } + else if (params->result_buf_size >= last_result_len + 1) + { + memcpy(params->result_buf, last_result, last_result_len + 1); + p_vosk_recognizer_reset(recognizer); + + free (last_result); + last_result = NULL; + + return STATUS_SUCCESS; + } + + params->result_buf_size = last_result_len + 1; + return STATUS_BUFFER_TOO_SMALL; +} + #else /* SONAME_LIBVOSK */ #define MAKE_UNSUPPORTED_FUNC( f ) \ @@ -268,6 +341,8 @@ MAKE_UNSUPPORTED_FUNC(process_attach) MAKE_UNSUPPORTED_FUNC(process_detach) MAKE_UNSUPPORTED_FUNC(speech_create_recognizer) MAKE_UNSUPPORTED_FUNC(speech_release_recognizer) +MAKE_UNSUPPORTED_FUNC(speech_recognize_audio) +MAKE_UNSUPPORTED_FUNC(speech_get_recognition_result) #undef MAKE_UNSUPPORTED_FUNC #endif /* SONAME_LIBVOSK */ @@ -278,6 +353,8 @@ unixlib_entry_t __wine_unix_call_funcs[] = process_detach, speech_create_recognizer, speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, }; unixlib_entry_t __wine_unix_call_wow64_funcs[] = @@ -286,4 +363,6 @@ unixlib_entry_t __wine_unix_call_wow64_funcs[] = process_detach, speech_create_recognizer, speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, }; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 974e8d5f797..a07d76705d5 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -44,12 +44,36 @@ struct speech_release_recognizer_params speech_recognizer_handle handle; }; -enum unix_funcs +enum speech_recognition_status +{ + RECOGNITION_STATUS_CONTINUING, + RECOGNITION_STATUS_RESULT_AVAILABLE, + RECOGNITION_STATUS_EXCEPTION, +}; + +struct speech_recognize_audio_params +{ + speech_recognizer_handle handle; + const BYTE *samples; + UINT32 samples_size; + enum speech_recognition_status status; +}; + +struct speech_get_recognition_result_params +{ + speech_recognizer_handle handle; + char *result_buf; + UINT32 result_buf_size; +}; + +enum vosk_funcs { unix_process_attach, unix_process_detach, unix_speech_create_recognizer, unix_speech_release_recognizer, + unix_speech_recognize_audio, + unix_speech_get_recognition_result, }; #endif From 24602f8c16acf47c40fb46e49e07ebadb8c7f397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Wed, 25 May 2022 14:14:18 +0200 Subject: [PATCH 574/758] windows.media.speech: Compare recognized words with available constraints. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 115 ++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index eb5fd9277a4..3c1ded1d349 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -192,18 +192,105 @@ static inline struct session *impl_from_ISpeechContinuousRecognitionSession( ISp return CONTAINING_RECORD(iface, struct session, ISpeechContinuousRecognitionSession_iface); } +static HRESULT session_find_constraint_by_string(struct session *session, WCHAR *str, HSTRING *hstr_out, ISpeechRecognitionConstraint **out) +{ + ISpeechRecognitionListConstraint *list_constraint; + IIterable_IInspectable *constraints_iterable; + IIterator_IInspectable *constraints_iterator; + ISpeechRecognitionConstraint *constraint; + IIterable_HSTRING *commands_iterable; + IIterator_HSTRING *commands_iterator; + BOOL has_constraint, has_command; + IVector_HSTRING *commands; + const WCHAR *command_str; + HSTRING command; + HRESULT hr; + + TRACE("session %p, str %s, out %p.\n", session, debugstr_w(str), out); + + if (FAILED(hr = IVector_ISpeechRecognitionConstraint_QueryInterface(session->constraints, &IID_IIterable_ISpeechRecognitionConstraint, (void **)&constraints_iterable))) + return hr; + + if (FAILED(hr = IIterable_IInspectable_First(constraints_iterable, &constraints_iterator))) + { + IIterable_IInspectable_Release(constraints_iterable); + return hr; + } + + *out = NULL; + + for (hr = IIterator_IInspectable_get_HasCurrent(constraints_iterator, &has_constraint); SUCCEEDED(hr) && has_constraint && !(*out); hr = IIterator_IInspectable_MoveNext(constraints_iterator, &has_constraint)) + { + list_constraint = NULL; + commands_iterable = NULL; + commands_iterator = NULL; + commands = NULL; + + if (FAILED(IIterator_IInspectable_get_Current(constraints_iterator, (IInspectable **)&constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionConstraint_QueryInterface(constraint, &IID_ISpeechRecognitionListConstraint, (void**)&list_constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionListConstraint_get_Commands(list_constraint, &commands))) + goto skip; + + if (FAILED(IVector_HSTRING_QueryInterface(commands, &IID_IIterable_HSTRING, (void **)&commands_iterable))) + goto skip; + + if (FAILED(IIterable_HSTRING_First(commands_iterable, &commands_iterator))) + goto skip; + + for (hr = IIterator_HSTRING_get_HasCurrent(commands_iterator, &has_command); SUCCEEDED(hr) && has_command && !(*out); hr = IIterator_HSTRING_MoveNext(commands_iterator, &has_command)) + { + if (FAILED(IIterator_HSTRING_get_Current(commands_iterator, &command))) + continue; + + command_str = WindowsGetStringRawBuffer(command, NULL); + + TRACE("Comparing str %s to command_str %s.\n", debugstr_w(str), debugstr_w(command_str)); + + if (!wcsicmp(str, command_str)) + { + TRACE("constraint %p has str %s.\n", constraint, debugstr_w(str)); + ISpeechRecognitionConstraint_AddRef((*out = constraint)); + WindowsDuplicateString(command, hstr_out); + } + + WindowsDeleteString(command); + } + +skip: + if (commands_iterator) IIterator_HSTRING_Release(commands_iterator); + if (commands_iterable) IIterable_HSTRING_Release(commands_iterable); + if (commands) IVector_HSTRING_Release(commands); + + if (list_constraint) ISpeechRecognitionListConstraint_Release(list_constraint); + if (constraint) ISpeechRecognitionConstraint_Release(constraint); + } + + IIterator_IInspectable_Release(constraints_iterator); + IIterable_IInspectable_Release(constraints_iterable); + + hr = (*out) ? S_OK : COR_E_KEYNOTFOUND; + return hr; +} + static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); struct speech_get_recognition_result_params recognition_result_params; struct speech_recognize_audio_params recognize_audio_params; + ISpeechRecognitionConstraint *constraint; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf; + WCHAR *recognized_text; DWORD flags, status; NTSTATUS nt_status; HANDLE events[2]; + HSTRING hstring; HRESULT hr; SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker"); @@ -253,6 +340,7 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { SIZE_T packet_size = 0, tmp_buf_offset = 0; UINT32 frames_available = 0; + INT recognized_text_len = 0; while (tmp_buf_offset < tmp_buf_size && IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK) @@ -300,8 +388,33 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) break; } - /* TODO: Compare recognized text to available options. */ + /* Silence was recognized. */ + if (!strcmp(recognition_result_params.result_buf, "")) + { + free(recognition_result_params.result_buf); + continue; + } + + recognized_text_len = MultiByteToWideChar(CP_UTF8, 0, recognition_result_params.result_buf, -1, NULL, 0); + + if (!(recognized_text = malloc(recognized_text_len * sizeof(WCHAR)))) + { + free(recognition_result_params.result_buf); + WARN("memory allocation failed.\n"); + break; + } + + MultiByteToWideChar(CP_UTF8, 0, recognition_result_params.result_buf, -1, recognized_text, recognized_text_len); + + if (SUCCEEDED(hr = session_find_constraint_by_string(impl, recognized_text, &hstring, &constraint))) + { + /* TODO: Send event. */ + + WindowsDeleteString(hstring); + ISpeechRecognitionConstraint_Release(constraint); + } + free(recognized_text); free(recognition_result_params.result_buf); } else From 11b6c0e2cef3e9c3ba6a4a8e0add633846a7c61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:36:51 +0200 Subject: [PATCH 575/758] windows.media.speech: Send event on recognition success. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 377 ++++++++++++++++++++++++- 1 file changed, 376 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 3c1ded1d349..0f38bcca1f3 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,372 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +static const char *debugstr_hstring(HSTRING hstr) +{ + const WCHAR *str; + UINT32 len; + if (hstr && !((ULONG_PTR)hstr >> 16)) return "(invalid)"; + str = WindowsGetStringRawBuffer(hstr, &len); + return wine_dbgstr_wn(str, len); +} + +struct recognition_result +{ + ISpeechRecognitionResult ISpeechRecognitionResult_iface; + ISpeechRecognitionResult2 ISpeechRecognitionResult2_iface; + LONG ref; + + ISpeechRecognitionConstraint *constraint; + HSTRING text; +}; + +static inline struct recognition_result *impl_from_ISpeechRecognitionResult( ISpeechRecognitionResult *iface ) +{ + return CONTAINING_RECORD(iface, struct recognition_result, ISpeechRecognitionResult_iface); +} + +static HRESULT WINAPI recognition_result_QueryInterface( ISpeechRecognitionResult *iface, REFIID iid, void **out ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(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_ISpeechRecognitionResult)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionResult_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechRecognitionResult2)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionResult2_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI recognition_result_AddRef( ISpeechRecognitionResult *iface ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI recognition_result_Release( ISpeechRecognitionResult *iface ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + { + ISpeechRecognitionConstraint_Release(impl->constraint); + WindowsDeleteString(impl->text); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI recognition_result_GetIids( ISpeechRecognitionResult *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 recognition_result_GetRuntimeClassName( ISpeechRecognitionResult *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetTrustLevel( ISpeechRecognitionResult *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_get_Status( ISpeechRecognitionResult *iface, SpeechRecognitionResultStatus *value ) +{ + FIXME("iface %p, operation %p stub!\n", iface, value); + *value = SpeechRecognitionResultStatus_Success; + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_Text( ISpeechRecognitionResult *iface, HSTRING *value ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + TRACE("iface %p, operation %p, text: %s.\n", iface, value, debugstr_hstring(impl->text)); + return WindowsDuplicateString(impl->text, value); +} + +static HRESULT WINAPI recognition_result_get_Confidence( ISpeechRecognitionResult *iface, SpeechRecognitionConfidence *value ) +{ + FIXME("iface %p, operation %p semi stub!\n", iface, value); + *value = SpeechRecognitionConfidence_High; + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_SemanticInterpretation( ISpeechRecognitionResult *iface, + ISpeechRecognitionSemanticInterpretation **value ) +{ + FIXME("iface %p, operation %p stub!\n", iface, value); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetAlternates( ISpeechRecognitionResult *iface, + UINT32 max_amount, + IVectorView_SpeechRecognitionResult **results ) +{ + IVector_IInspectable *vector; + struct vector_iids constraints_iids = + { + .iterable = &IID_IVectorView_SpeechRecognitionResult, + .iterator = &IID_IVectorView_SpeechRecognitionResult, + .vector = &IID_IVector_IInspectable, + .view = &IID_IVectorView_SpeechRecognitionResult, + }; + + FIXME("iface %p, max_amount %u, results %p stub!\n", iface, max_amount, results); + + vector_inspectable_create(&constraints_iids, (IVector_IInspectable **)&vector); + IVector_IInspectable_GetView(vector, (IVectorView_IInspectable **)results); + IVector_IInspectable_Release(vector); + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_Constraint( ISpeechRecognitionResult *iface, ISpeechRecognitionConstraint **value ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + TRACE("iface %p, operation %p.\n", iface, value); + ISpeechRecognitionConstraint_AddRef((*value = impl->constraint)); + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_RulePath( ISpeechRecognitionResult *iface, IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_get_RawConfidence( ISpeechRecognitionResult *iface, DOUBLE *value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct ISpeechRecognitionResultVtbl recognition_result_vtbl = +{ + /* IUnknown methods */ + recognition_result_QueryInterface, + recognition_result_AddRef, + recognition_result_Release, + /* IInspectable methods */ + recognition_result_GetIids, + recognition_result_GetRuntimeClassName, + recognition_result_GetTrustLevel, + /* ISpeechRecognitionResult methods */ + recognition_result_get_Status, + recognition_result_get_Text, + recognition_result_get_Confidence, + recognition_result_get_SemanticInterpretation, + recognition_result_GetAlternates, + recognition_result_get_Constraint, + recognition_result_get_RulePath, + recognition_result_get_RawConfidence +}; + +DEFINE_IINSPECTABLE(recognition_result2, ISpeechRecognitionResult2, struct recognition_result, ISpeechRecognitionResult_iface) + +static HRESULT WINAPI recognition_result2_get_PhraseStartTime( ISpeechRecognitionResult2 *iface, DateTime *value ) +{ + DateTime dt = { .UniversalTime = 0 }; + FIXME("iface %p, value %p stub!\n", iface, value); + *value = dt; + return S_OK; +} + + +static HRESULT WINAPI recognition_result2_get_PhraseDuration( ISpeechRecognitionResult2 *iface, TimeSpan *value ) +{ + TimeSpan ts = { .Duration = 50000000LL }; /* Use 5 seconds as stub value. */ + FIXME("iface %p, value %p stub!\n", iface, value); + *value = ts; + return S_OK; +} + +static const struct ISpeechRecognitionResult2Vtbl recognition_result2_vtbl = +{ + /* IUnknown methods */ + recognition_result2_QueryInterface, + recognition_result2_AddRef, + recognition_result2_Release, + /* IInspectable methods */ + recognition_result2_GetIids, + recognition_result2_GetRuntimeClassName, + recognition_result2_GetTrustLevel, + /* ISpeechRecognitionResult2 methods */ + recognition_result2_get_PhraseStartTime, + recognition_result2_get_PhraseDuration +}; + +static HRESULT WINAPI recognition_result_create( ISpeechRecognitionConstraint *constraint, + HSTRING result_text, + ISpeechRecognitionResult **out ) +{ + struct recognition_result *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechRecognitionResult_iface.lpVtbl = &recognition_result_vtbl; + impl->ISpeechRecognitionResult2_iface.lpVtbl = &recognition_result2_vtbl; + impl->ref = 1; + + if (constraint) ISpeechRecognitionConstraint_AddRef((impl->constraint = constraint)); + WindowsDuplicateString(result_text, &impl->text); + + *out = &impl->ISpeechRecognitionResult_iface; + + TRACE("created %p.\n", *out); + + return S_OK; +} + +struct recognition_result_event_args +{ + ISpeechContinuousRecognitionResultGeneratedEventArgs ISpeechContinuousRecognitionResultGeneratedEventArgs_iface; + LONG ref; + + ISpeechRecognitionResult *result; +}; + +static inline struct recognition_result_event_args *impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + return CONTAINING_RECORD(iface, struct recognition_result_event_args, ISpeechContinuousRecognitionResultGeneratedEventArgs_iface); +} + +static HRESULT WINAPI recognition_result_event_args_QueryInterface( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, REFIID iid, void **out ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(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_ISpeechContinuousRecognitionResultGeneratedEventArgs)) + { + IInspectable_AddRef((*out = &impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI recognition_result_event_args_AddRef( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI recognition_result_event_args_Release( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if (!ref) + { + if (impl->result) ISpeechRecognitionResult_Release(impl->result); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI recognition_result_event_args_GetIids( ISpeechContinuousRecognitionResultGeneratedEventArgs *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 recognition_result_event_args_GetRuntimeClassName( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_GetTrustLevel( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_get_Result( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, + ISpeechRecognitionResult **value ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + FIXME("iface %p value %p stub!\n", iface, value); + ISpeechRecognitionResult_AddRef((*value = impl->result)); + return S_OK; +} + +static const struct ISpeechContinuousRecognitionResultGeneratedEventArgsVtbl recognition_result_event_args_vtbl = +{ + /* IUnknown methods */ + recognition_result_event_args_QueryInterface, + recognition_result_event_args_AddRef, + recognition_result_event_args_Release, + /* IInspectable methods */ + recognition_result_event_args_GetIids, + recognition_result_event_args_GetRuntimeClassName, + recognition_result_event_args_GetTrustLevel, + /* ISpeechContinuousRecognitionResultGeneratedEventArgs methods */ + recognition_result_event_args_get_Result +}; + +static HRESULT WINAPI recognition_result_event_args_create( ISpeechRecognitionResult *result, + ISpeechContinuousRecognitionResultGeneratedEventArgs **out ) +{ + struct recognition_result_event_args *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface.lpVtbl = &recognition_result_event_args_vtbl; + impl->ref = 1; + if (result) ISpeechRecognitionResult_AddRef((impl->result = result)); + + *out = &impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface; + + TRACE("created %p.\n", *out); + return S_OK; +} + /* * * ISpeechRecognitionCompilationResult @@ -282,7 +648,9 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); struct speech_get_recognition_result_params recognition_result_params; struct speech_recognize_audio_params recognize_audio_params; + ISpeechContinuousRecognitionResultGeneratedEventArgs *event_args; ISpeechRecognitionConstraint *constraint; + ISpeechRecognitionResult *result; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf; @@ -408,8 +776,15 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) if (SUCCEEDED(hr = session_find_constraint_by_string(impl, recognized_text, &hstring, &constraint))) { - /* TODO: Send event. */ + recognition_result_create(constraint, hstring, &result); + recognition_result_event_args_create(result, &event_args); + + typed_event_handlers_notify(&impl->result_handlers, + (IInspectable *)&impl->ISpeechContinuousRecognitionSession_iface, + (IInspectable *)event_args); + ISpeechContinuousRecognitionResultGeneratedEventArgs_Release(event_args); + ISpeechRecognitionResult_Release(result); WindowsDeleteString(hstring); ISpeechRecognitionConstraint_Release(constraint); } From 1b6e03a0352e5cddacd634aa703f8f26fe29daa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:37:41 +0200 Subject: [PATCH 576/758] windows.media.speech: Add ISpeechRecognitionSemanticInterpretation stub. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 112 ++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 0f38bcca1f3..3d434068148 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -39,6 +39,116 @@ static const char *debugstr_hstring(HSTRING hstr) return wine_dbgstr_wn(str, len); } +struct semantic_interpretation +{ + ISpeechRecognitionSemanticInterpretation ISpeechRecognitionSemanticInterpretation_iface; + LONG ref; +}; + +static inline struct semantic_interpretation *impl_from_ISpeechRecognitionSemanticInterpretation( ISpeechRecognitionSemanticInterpretation *iface ) +{ + return CONTAINING_RECORD(iface, struct semantic_interpretation, ISpeechRecognitionSemanticInterpretation_iface); +} + +HRESULT WINAPI semantic_interpretation_QueryInterface( ISpeechRecognitionSemanticInterpretation *iface, REFIID iid, void **out ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(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_ISpeechRecognitionSemanticInterpretation)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionSemanticInterpretation_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI semantic_interpretation_AddRef( ISpeechRecognitionSemanticInterpretation *iface ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +ULONG WINAPI semantic_interpretation_Release( ISpeechRecognitionSemanticInterpretation *iface ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + free(impl); + + return ref; +} + +HRESULT WINAPI semantic_interpretation_GetIids( ISpeechRecognitionSemanticInterpretation *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_GetRuntimeClassName( ISpeechRecognitionSemanticInterpretation *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_GetTrustLevel( ISpeechRecognitionSemanticInterpretation *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_get_Properties( ISpeechRecognitionSemanticInterpretation *iface, IMapView_HSTRING_IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct ISpeechRecognitionSemanticInterpretationVtbl semantic_interpretation_vtbl = +{ + /* IUnknown methods */ + semantic_interpretation_QueryInterface, + semantic_interpretation_AddRef, + semantic_interpretation_Release, + /* IInspectable methods */ + semantic_interpretation_GetIids, + semantic_interpretation_GetRuntimeClassName, + semantic_interpretation_GetTrustLevel, + /* ISpeechRecognitionSemanticInterpretation methods */ + semantic_interpretation_get_Properties +}; + + +static HRESULT semantic_interpretation_create( ISpeechRecognitionSemanticInterpretation **out ) +{ + struct semantic_interpretation *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechRecognitionSemanticInterpretation_iface.lpVtbl = &semantic_interpretation_vtbl; + impl->ref = 1; + + *out = &impl->ISpeechRecognitionSemanticInterpretation_iface; + TRACE("created %p\n", *out); + return S_OK; +} + struct recognition_result { ISpeechRecognitionResult ISpeechRecognitionResult_iface; @@ -148,7 +258,7 @@ static HRESULT WINAPI recognition_result_get_SemanticInterpretation( ISpeechReco ISpeechRecognitionSemanticInterpretation **value ) { FIXME("iface %p, operation %p stub!\n", iface, value); - return E_NOTIMPL; + return semantic_interpretation_create(value); } static HRESULT WINAPI recognition_result_GetAlternates( ISpeechRecognitionResult *iface, From ba684540fe3a2a0e1f003109fc3080460e7f763d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:44:05 +0200 Subject: [PATCH 577/758] HACK: windows.media.speech: Stub semantic_interpretation_get_Properties. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 134 ++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 3d434068148..165f4f4a9ee 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -39,6 +39,138 @@ static const char *debugstr_hstring(HSTRING hstr) return wine_dbgstr_wn(str, len); } +struct map_view_hstring_vector_view_hstring +{ + IMapView_HSTRING_IVectorView_HSTRING IMapView_HSTRING_IVectorView_HSTRING_iface; + LONG ref; +}; + +static inline struct map_view_hstring_vector_view_hstring *impl_from_IMapView_HSTRING_IVectorView_HSTRING( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + return CONTAINING_RECORD(iface, struct map_view_hstring_vector_view_hstring, IMapView_HSTRING_IVectorView_HSTRING_iface); +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_QueryInterface( IMapView_HSTRING_IVectorView_HSTRING *iface, REFIID iid, void **out ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(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_IMapView_HSTRING_IVectorView_HSTRING)) + { + IInspectable_AddRef((*out = &impl->IMapView_HSTRING_IVectorView_HSTRING_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI map_view_hstring_vector_view_hstring_AddRef( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +ULONG WINAPI map_view_hstring_vector_view_hstring_Release( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + free(impl); + + return ref; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetIids( IMapView_HSTRING_IVectorView_HSTRING *iface, ULONG *iidCount, IID **iids ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetRuntimeClassName( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING *className ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetTrustLevel( IMapView_HSTRING_IVectorView_HSTRING *iface, TrustLevel *trustLevel ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_Lookup( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING key, IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_get_Size( IMapView_HSTRING_IVectorView_HSTRING *iface, unsigned int *size ) +{ + FIXME("iface %p stub!\n", iface); + *size = 0; + return S_OK; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_HasKey( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING key, boolean *found ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_Split( IMapView_HSTRING_IVectorView_HSTRING *iface, IMapView_HSTRING_IVectorView_HSTRING **first, IMapView_HSTRING_IVectorView_HSTRING **second ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct IMapView_HSTRING_IVectorView_HSTRINGVtbl map_view_hstring_vector_view_hstring_vtbl = +{ + /* IUnknown methods */ + map_view_hstring_vector_view_hstring_QueryInterface, + map_view_hstring_vector_view_hstring_AddRef, + map_view_hstring_vector_view_hstring_Release, + /* IInspectable methods */ + map_view_hstring_vector_view_hstring_GetIids, + map_view_hstring_vector_view_hstring_GetRuntimeClassName, + map_view_hstring_vector_view_hstring_GetTrustLevel, + /* IMapView* > methods */ + map_view_hstring_vector_view_hstring_Lookup, + map_view_hstring_vector_view_hstring_get_Size, + map_view_hstring_vector_view_hstring_HasKey, + map_view_hstring_vector_view_hstring_Split +}; + + +static HRESULT map_view_hstring_vector_view_hstring_create( IMapView_HSTRING_IVectorView_HSTRING **out ) +{ + struct map_view_hstring_vector_view_hstring *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->IMapView_HSTRING_IVectorView_HSTRING_iface.lpVtbl = &map_view_hstring_vector_view_hstring_vtbl; + impl->ref = 1; + + *out = &impl->IMapView_HSTRING_IVectorView_HSTRING_iface; + TRACE("created %p\n", *out); + return S_OK; +} + struct semantic_interpretation { ISpeechRecognitionSemanticInterpretation ISpeechRecognitionSemanticInterpretation_iface; @@ -111,7 +243,7 @@ HRESULT WINAPI semantic_interpretation_GetTrustLevel( ISpeechRecognitionSemantic HRESULT WINAPI semantic_interpretation_get_Properties( ISpeechRecognitionSemanticInterpretation *iface, IMapView_HSTRING_IVectorView_HSTRING **value ) { FIXME("iface %p stub!\n", iface); - return E_NOTIMPL; + return map_view_hstring_vector_view_hstring_create(value); } static const struct ISpeechRecognitionSemanticInterpretationVtbl semantic_interpretation_vtbl = From 6a54c9170f803a53fc717409bb75f3d74455c5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 28 Feb 2023 16:11:36 +0100 Subject: [PATCH 578/758] WIP: windows.media.speech: Implement grammar. CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 106 ++++++++++++++++++++++++- dlls/windows.media.speech/unixlib.c | 61 +++++++++++++- dlls/windows.media.speech/unixlib.h | 2 + 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 165f4f4a9ee..218453a2203 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -1497,7 +1497,7 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } -static HRESULT recognizer_create_unix_instance( struct session *session ) +static HRESULT recognizer_create_unix_instance( struct session *session, const char **grammar, UINT32 grammar_size ) { struct speech_create_recognizer_params create_params = { 0 }; WCHAR locale[LOCALE_NAME_MAX_LENGTH]; @@ -1514,6 +1514,8 @@ static HRESULT recognizer_create_unix_instance( struct session *session ) return HRESULT_FROM_WIN32(GetLastError()); create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + create_params.grammar = grammar; + create_params.grammar_size = grammar_size; if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) { @@ -1530,11 +1532,109 @@ static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IIns { struct recognizer *impl = impl_from_ISpeechRecognizer((ISpeechRecognizer *)invoker); struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + struct speech_release_recognizer_params release_params; + ISpeechRecognitionListConstraint *list_constraint; + IIterable_IInspectable *constraints_iterable; + IIterator_IInspectable *constraints_iterator; + ISpeechRecognitionConstraint *constraint; + IIterable_HSTRING *commands_iterable; + IIterator_HSTRING *commands_iterator; + BOOL has_constraint, has_command; + IVector_HSTRING *commands; + const WCHAR *command_str; + UINT32 grammar_size = 0, i = 0; + char **grammar = NULL; + HSTRING command; + UINT32 size = 0; HRESULT hr; - if (FAILED(hr = recognizer_create_unix_instance(session))) + if (FAILED(hr = IVector_ISpeechRecognitionConstraint_QueryInterface(session->constraints, &IID_IIterable_ISpeechRecognitionConstraint, (void **)&constraints_iterable))) + return hr; + + if (FAILED(hr = IIterable_IInspectable_First(constraints_iterable, &constraints_iterator))) + { + IIterable_IInspectable_Release(constraints_iterable); + return hr; + } + + for (hr = IIterator_IInspectable_get_HasCurrent(constraints_iterator, &has_constraint); SUCCEEDED(hr) && has_constraint; hr = IIterator_IInspectable_MoveNext(constraints_iterator, &has_constraint)) + { + list_constraint = NULL; + commands_iterable = NULL; + commands_iterator = NULL; + commands = NULL; + + if (FAILED(IIterator_IInspectable_get_Current(constraints_iterator, (IInspectable **)&constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionConstraint_QueryInterface(constraint, &IID_ISpeechRecognitionListConstraint, (void**)&list_constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionListConstraint_get_Commands(list_constraint, &commands))) + goto skip; + + if (FAILED(IVector_HSTRING_QueryInterface(commands, &IID_IIterable_HSTRING, (void **)&commands_iterable))) + goto skip; + + if (FAILED(IIterable_HSTRING_First(commands_iterable, &commands_iterator))) + goto skip; + + if (FAILED(IVector_HSTRING_get_Size(commands, &size))) + goto skip; + + grammar_size += size; + grammar = realloc(grammar, grammar_size * sizeof(char *)); + + for (hr = IIterator_HSTRING_get_HasCurrent(commands_iterator, &has_command); SUCCEEDED(hr) && has_command; hr = IIterator_HSTRING_MoveNext(commands_iterator, &has_command)) + { + if (FAILED(IIterator_HSTRING_get_Current(commands_iterator, &command))) + continue; + + command_str = WindowsGetStringRawBuffer(command, NULL); + + if (command_str) + { + WCHAR *wstr = wcsdup(command_str); + size_t len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, grammar[i], 0, NULL, NULL); + grammar[i] = malloc(len * sizeof(char)); + + CharLowerW(wstr); + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, grammar[i], len, NULL, NULL); + free(wstr); + i++; + } + + WindowsDeleteString(command); + } + +skip: + if (commands_iterator) IIterator_HSTRING_Release(commands_iterator); + if (commands_iterable) IIterable_HSTRING_Release(commands_iterable); + if (commands) IVector_HSTRING_Release(commands); + + if (list_constraint) ISpeechRecognitionListConstraint_Release(list_constraint); + if (constraint) ISpeechRecognitionConstraint_Release(constraint); + } + + IIterator_IInspectable_Release(constraints_iterator); + IIterable_IInspectable_Release(constraints_iterable); + + if (session->unix_handle) + { + release_params.handle = session->unix_handle; + WINE_UNIX_CALL(unix_speech_release_recognizer, &release_params); + session->unix_handle = 0; + } + + hr = recognizer_create_unix_instance(session, (const char **)grammar, grammar_size); + + for(i = 0; i < grammar_size; ++i) + free(grammar[i]); + free(grammar); + + if (FAILED(hr)) { - WARN("Failed to created recognizer instance.\n"); + WARN("Failed to created recognizer instance with grammar.\n"); return compilation_result_create(SpeechRecognitionResultStatus_GrammarCompilationFailure, (ISpeechRecognitionCompilationResult **) result); } else return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 177256b2060..92dec9e2908 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -55,6 +55,7 @@ static void *vosk_handle; MAKE_FUNCPTR(vosk_model_new); MAKE_FUNCPTR(vosk_model_free); MAKE_FUNCPTR(vosk_recognizer_new); +MAKE_FUNCPTR(vosk_recognizer_new_grm); MAKE_FUNCPTR(vosk_recognizer_free); MAKE_FUNCPTR(vosk_recognizer_accept_waveform); MAKE_FUNCPTR(vosk_recognizer_final_result); @@ -77,6 +78,8 @@ static NTSTATUS process_attach( void *args ) goto error; \ } LOAD_FUNCPTR(vosk_model_new) + LOAD_FUNCPTR(vosk_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_new_grm) LOAD_FUNCPTR(vosk_model_free) LOAD_FUNCPTR(vosk_recognizer_new) LOAD_FUNCPTR(vosk_recognizer_free) @@ -222,12 +225,58 @@ static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) return status; } +static NTSTATUS grammar_to_json_array(const char **grammar, UINT32 grammar_size, const char **array) +{ + size_t buf_size = strlen("[]") + 1, len; + char *buf; + UINT32 i; + + for (i = 0; i < grammar_size; ++i) + { + buf_size += strlen(grammar[i]) + 4; /* (4) - two double quotes, a comma and a space */ + } + + if (!(buf = malloc(buf_size))) + return STATUS_NO_MEMORY; + + *array = buf; + + *buf = '['; + buf++; + + for (i = 0; i < grammar_size; ++i) + { + *buf = '\"'; + buf++; + len = strlen(grammar[i]); + memcpy(buf, grammar[i], len); + buf += len; + *buf = '\"'; + buf++; + if (i < (grammar_size - 1)) + { + *buf = ','; + buf++; + *buf = ' '; + buf++; + } + } + + *buf = ']'; + buf++; + *buf = '\0'; + + TRACE("created json array %s.\n", debugstr_a(*array)); + return STATUS_SUCCESS; +} + static NTSTATUS speech_create_recognizer( void *args ) { struct speech_create_recognizer_params *params = args; VoskRecognizer *recognizer = NULL; VoskModel *model = NULL; NTSTATUS status = STATUS_SUCCESS; + const char *grammar_json; TRACE("args %p.\n", args); @@ -237,8 +286,16 @@ static NTSTATUS speech_create_recognizer( void *args ) if ((status = find_model_by_locale(params->locale, &model))) return status; - if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) - status = STATUS_UNSUCCESSFUL; + if (params->grammar && grammar_to_json_array(params->grammar, params->grammar_size, &grammar_json) == STATUS_SUCCESS) + { + if (!(recognizer = p_vosk_recognizer_new_grm(model, params->sample_rate, grammar_json))) + status = STATUS_UNSUCCESSFUL; + } + else + { + if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + } /* VoskModel is reference-counted. A VoskRecognizer keeps a reference to its model. */ p_vosk_model_free(model); diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index a07d76705d5..ad2fab738b9 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -37,6 +37,8 @@ struct speech_create_recognizer_params speech_recognizer_handle handle; CHAR locale[LOCALE_NAME_MAX_LENGTH]; FLOAT sample_rate; + const char **grammar; + unsigned int grammar_size; }; struct speech_release_recognizer_params From f8d8bb76f613cfeec541e072caf6391a78840f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 28 Feb 2023 22:06:23 +0100 Subject: [PATCH 579/758] HACK: windows.media.speech/tests: Add tests for manual recognition testing. Speak during the delay, to test the code. CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 57e216b78d5..be616e34d62 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -211,7 +211,23 @@ HRESULT WINAPI recognition_result_handler_Invoke( IHandler_RecognitionResult *if ISpeechContinuousRecognitionSession *sender, ISpeechContinuousRecognitionResultGeneratedEventArgs *args ) { - trace("iface %p, sender %p, args %p.\n", iface, sender, args); + ISpeechRecognitionResult *result; + HSTRING hstring; + HRESULT hr; + + if (!args) return S_OK; + + hr = ISpeechContinuousRecognitionResultGeneratedEventArgs_get_Result(args, &result); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ISpeechRecognitionResult_get_Text(result, &hstring); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + trace("iface %p, sender %p, args %p, text %s.\n", iface, sender, args, debugstr_w(WindowsGetStringRawBuffer(hstring, NULL))); + + WindowsDeleteString(hstring); + ISpeechRecognitionResult_Release(result); + return S_OK; } @@ -1599,7 +1615,7 @@ static void test_Recognition(void) static const WCHAR *list_constraint_name = L"Windows.Media.SpeechRecognition.SpeechRecognitionListConstraint"; static const WCHAR *recognizer_name = L"Windows.Media.SpeechRecognition.SpeechRecognizer"; static const WCHAR *speech_constraint_tag = L"test_message"; - static const WCHAR *speech_constraints[] = { L"This is a test.", L"Number 5!", L"What time is it?" }; + static const WCHAR *speech_constraints[] = { L"This is a test", L"Number 5", L"What time is it" }; ISpeechRecognitionListConstraintFactory *listconstraint_factory = NULL; IAsyncOperation_SpeechRecognitionCompilationResult *operation = NULL; IVector_ISpeechRecognitionConstraint *constraints = NULL; @@ -1773,6 +1789,8 @@ static void test_Recognition(void) ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); ok(recog_state == SpeechRecognizerState_Capturing, "recog_state was %u.\n", recog_state); + + Sleep(10000); /* * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. */ From a2eced06295e6c92cee4fcd7fa051a2ff18ef6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Mar 2023 15:31:08 +0100 Subject: [PATCH 580/758] HACK: windows.media.speech: Load Vosk models from Phasmophobia. CW-Bug-Id: #20134 --- dlls/windows.media.speech/unixlib.c | 67 +++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 92dec9e2908..38ec8d3ca90 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -116,13 +116,55 @@ static inline VoskRecognizer *vosk_recognizer_from_handle( speech_recognizer_han return (VoskRecognizer *)(UINT_PTR)handle; } +static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) +{ + if (!strncmp(lang, "ar", len)) + return "Arabic"; + if (!strncmp(lang, "ca", len)) + return "Catalan"; + if (!strncmp(lang, "zn", len)) + return "Chinese"; + if (!strncmp(lang, "cs", len)) + return "Czech"; + if (!strncmp(lang, "nl", len)) + return "Dutch"; + if (!strncmp(lang, "en", len)) + return "English"; + if (!strncmp(lang, "fr", len)) + return "French"; + if (!strncmp(lang, "de", len)) + return "German"; + if (!strncmp(lang, "de", len)) + return "German"; + if (!strncmp(lang, "el", len)) + return "Greek"; + if (!strncmp(lang, "it", len)) + return "Italian"; + if (!strncmp(lang, "ja", len)) + return "Japanese"; + if (!strncmp(lang, "pt", len)) + return "Portuguese"; + if (!strncmp(lang, "ru", len)) + return "Russian"; + if (!strncmp(lang, "es", len)) + return "Spanish"; + if (!strncmp(lang, "sw", len)) + return "Swedish"; + if (!strncmp(lang, "tr", len)) + return "Turkish"; + if (!strncmp(lang, "uk", len)) + return "Ukrainian"; + + return ""; +} + static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) { static const char *vosk_model_identifier_small = "vosk-model-small-"; static const char *vosk_model_identifier = "vosk-model-"; size_t ident_small_len = strlen(vosk_model_identifier_small); size_t ident_len = strlen(vosk_model_identifier); - char *ent_name, *model_path, *best_match, *delim; + char *ent_name, *model_path, *best_match, *delim, *appid = getenv("SteamAppId"), *str = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; struct dirent *dirent; size_t path_len, len; @@ -149,7 +191,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc ent_name += ident_small_len; else if (!strncmp(ent_name, vosk_model_identifier, ident_len)) ent_name += ident_len; - else + else if (strcmp(appid, "739630") != 0) continue; /* @@ -165,6 +207,12 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc if (!best_match && !strncmp(ent_name, locale, delim - locale)) best_match = strdup(dirent->d_name); + + if (!best_match && !strcmp(appid, "739630")) + { + if ((str = (char *)map_lang_to_phasmophobia_dir(locale, delim - locale))) + best_match = strdup(str); + } } closedir(dir); @@ -195,7 +243,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) { const char *suffix = NULL; - char *env, *path = NULL; + char *env, *path = NULL, *appid = getenv("SteamAppId"); NTSTATUS status; TRACE("locale %s, model %p.\n", debugstr_a(locale), model); @@ -222,6 +270,19 @@ static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) status = find_model_by_locale_and_path(path, locale, model); free(path); + /* Hack to load Vosk models from Phasmophobia, so they don't need to be downloaded separately.*/ + if (status && appid && !strcmp(appid, "739630") && (env = getenv("PWD"))) + { + suffix = "/Phasmophobia_Data/StreamingAssets/LanguageModels"; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + } + return status; } From 2ac31dcaf3026fa0093d8dc64f4129c8c37227df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Mar 2023 16:36:36 +0100 Subject: [PATCH 581/758] windows.media.speech: Suppress verbose Unixlib traces. CW-Bug-Id: #20134 --- dlls/windows.media.speech/unixlib.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 38ec8d3ca90..e98e2e69fb3 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -327,7 +327,6 @@ static NTSTATUS grammar_to_json_array(const char **grammar, UINT32 grammar_size, buf++; *buf = '\0'; - TRACE("created json array %s.\n", debugstr_a(*array)); return STATUS_SUCCESS; } @@ -405,8 +404,6 @@ static NTSTATUS speech_get_recognition_result( void* args ) static char *last_result = NULL; const char *tmp = NULL; - TRACE("args %p.\n", args); - if (!vosk_handle) return STATUS_NOT_SUPPORTED; @@ -427,7 +424,6 @@ static NTSTATUS speech_get_recognition_result( void* args ) last_result = (char *)tmp; last_result_len = strlen(last_result); - TRACE("last_result %s.\n", debugstr_a(last_result)); } else return STATUS_NOT_FOUND; } From d00bc9c35db18d4130387da0dd1c6fc5f3128201 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 28 Jun 2023 20:42:36 -0600 Subject: [PATCH 582/758] crypt32/tests: Add test for CryptVerifyCertificateSignature() with ECC public key. CW-Bug-Id: #22376 --- dlls/crypt32/tests/cert.c | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index 83594560efa..370ed064a94 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -2042,6 +2042,43 @@ static void testVerifyCertSigEx(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigne static BYTE emptyCert[] = { 0x30, 0x00 }; + +/* Generated with: + * openssl ecparam -name prime256v1 -genkey -out private-key.pem + * openssl req -new -x509 -key private-key.pem -out certificate.der -outform der -days 900000 -subj "/C=US/ST=T/L=T/O=T/CN=T" + */ +static const BYTE self_signed_ecc_prime256v1[] = { +0x30,0x82,0x01,0xd1,0x30,0x82,0x01,0x77,0xa0,0x03,0x02,0x01,0x02,0x02,0x14,0x32, +0xc2,0xbe,0x7b,0xa2,0x85,0x78,0x89,0x82,0xf8,0x10,0x66,0xd4,0x1d,0xd4,0x97,0x61, +0x83,0x02,0xc8,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30, +0x3d,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a, +0x30,0x08,0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03, +0x55,0x04,0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c, +0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30,0x20, +0x17,0x0d,0x32,0x33,0x30,0x36,0x32,0x39,0x30,0x32,0x32,0x34,0x30,0x33,0x5a,0x18, +0x0f,0x34,0x34,0x38,0x37,0x30,0x38,0x31,0x30,0x30,0x32,0x32,0x34,0x30,0x33,0x5a, +0x30,0x3d,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06, +0x03,0x55,0x04,0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a, +0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30, +0x59,0x30,0x13,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,0x86, +0x48,0xce,0x3d,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0xfe,0xdb,0x26,0x60,0xf6,0x89, +0x3d,0xa4,0x50,0x1f,0x06,0x91,0x4e,0x07,0x86,0x70,0x2b,0xc0,0x7c,0x5e,0xb3,0xca, +0xdc,0x1a,0x8b,0x82,0xdd,0x41,0x8a,0x62,0x0f,0xba,0xd1,0xd7,0x80,0xc8,0x20,0x77, +0xba,0xe7,0xe1,0x36,0xf8,0x76,0x9a,0x54,0x6a,0x1b,0x67,0x45,0x3b,0xd7,0x85,0x84, +0xbe,0x11,0xe6,0x6c,0x70,0xd8,0x18,0x68,0xd8,0xa7,0xa3,0x53,0x30,0x51,0x30,0x1d, +0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf,0x63, +0xa4,0x12,0x29,0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x1f,0x06, +0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf, +0x63,0xa4,0x12,0x29,0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x0f, +0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x05,0x30,0x03,0x01,0x01,0xff,0x30, +0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,0x48,0x00,0x30,0x45, +0x02,0x21,0x00,0x83,0xae,0xa2,0x23,0x95,0x1a,0x65,0x09,0x48,0x40,0x10,0xeb,0x94, +0x90,0x02,0xde,0xe3,0x0f,0x4b,0xd1,0x23,0x73,0xc6,0xd5,0x49,0xa8,0x9c,0x06,0x9c, +0xd3,0xfb,0xc1,0x02,0x20,0x0c,0xf3,0x92,0xec,0xc8,0xb5,0x7e,0x9c,0x14,0x5d,0xb0, +0x26,0xfd,0x2a,0x3c,0x4e,0x08,0x55,0x09,0x35,0x40,0x7c,0xf8,0xf9,0x1b,0x22,0x55, +0x08,0x9b,0x3f,0x37,0x29, }; + static void testCertSigs(void) { HCRYPTPROV csp; @@ -2049,6 +2086,7 @@ static void testCertSigs(void) BOOL ret; BYTE sig[64]; DWORD sigSize = sizeof(sig); + PCCERT_CONTEXT cert; /* Just in case a previous run failed, delete this thing */ CryptAcquireContextA(&csp, cspNameA, MS_DEF_PROV_A, PROV_RSA_FULL, @@ -2065,6 +2103,13 @@ static void testCertSigs(void) ret = CryptAcquireContextA(&csp, cspNameA, MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_DELETEKEYSET); ok(ret, "CryptAcquireContext failed: %08lx\n", GetLastError()); + + cert = CertCreateCertificateContext(X509_ASN_ENCODING, self_signed_ecc_prime256v1, sizeof(self_signed_ecc_prime256v1)); + ok(!!cert, "failed, error %#lx.\n", GetLastError()); + ret = CryptVerifyCertificateSignature(0, X509_ASN_ENCODING, self_signed_ecc_prime256v1, + sizeof(self_signed_ecc_prime256v1), &cert->pCertInfo->SubjectPublicKeyInfo); + ok(ret, "failed, error %#lx.\n", GetLastError()); + CertFreeCertificateContext(cert); } static const BYTE md5SignedEmptyCert[] = { From 2232016fa39533411de7cbbeb3ce767c20eb2752 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 29 Jun 2023 10:55:58 -0600 Subject: [PATCH 583/758] crypt32/tests: Test ECC message signature verification. CW-Bug-Id: #22376 --- dlls/crypt32/tests/msg.c | 140 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index 16f7402c613..c663909ed7c 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -3483,6 +3483,145 @@ static void test_msg_get_and_verify_signer(void) CryptMsgClose(msg); } +/* Generated with: + * openssl ecparam -name prime256v1 -genkey -out private-key.pem + * openssl req -new -x509 -key private-key.pem -out certificate.der -outform der -days 10000 -subj "/C=US/ST=T/L=T/O=T/CN=T" + * openssl pkcs12 -export -out certificate.pfx -inkey private-key.pem -in certificate.der + * - import certificate.pfx on Windows + * signtool /sign /v /fd SHA256 certificate.pfx a.exe + * - extract signed message from a.exe + */ +static const BYTE msg_signed_ecc_prime256v1[] = { +0x30,0x82,0x03,0x85,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x02,0xa0, +0x82,0x03,0x76,0x30,0x82,0x03,0x72,0x02,0x01,0x01,0x31,0x0f,0x30,0x0d,0x06,0x09, +0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x30,0x5c,0x06,0x0a,0x2b, +0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x04,0xa0,0x4e,0x30,0x4c,0x30,0x17,0x06, +0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0f,0x30,0x09,0x03,0x01,0x00, +0xa0,0x04,0xa2,0x02,0x80,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01, +0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20,0x32,0x54,0x6a,0x85,0xd7,0xe6,0x83, +0x46,0x6c,0x94,0x58,0x3b,0x17,0xa4,0xa8,0x8b,0xea,0xea,0x11,0xe0,0x6e,0xc4,0x3c, +0xea,0xde,0xbb,0x2e,0x7d,0xa3,0xb6,0xbe,0x69,0xa0,0x82,0x01,0xd5,0x30,0x82,0x01, +0xd1,0x30,0x82,0x01,0x77,0xa0,0x03,0x02,0x01,0x02,0x02,0x14,0x13,0x09,0x38,0x76, +0x3a,0x38,0xef,0x36,0xac,0xc3,0xa5,0x7e,0xa5,0xad,0x56,0x50,0x8d,0x77,0x55,0x2c, +0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30,0x3d,0x31,0x0b, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a,0x30,0x08,0x06, +0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x07, +0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c,0x01,0x54,0x31, +0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30,0x20,0x17,0x0d,0x32, +0x33,0x30,0x36,0x32,0x39,0x30,0x33,0x31,0x38,0x35,0x35,0x5a,0x18,0x0f,0x32,0x30, +0x35,0x30,0x31,0x31,0x31,0x34,0x30,0x33,0x31,0x38,0x35,0x35,0x5a,0x30,0x3d,0x31, +0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a,0x30,0x08, +0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04, +0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c,0x01,0x54, +0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30,0x59,0x30,0x13, +0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d, +0x03,0x01,0x07,0x03,0x42,0x00,0x04,0xfe,0xdb,0x26,0x60,0xf6,0x89,0x3d,0xa4,0x50, +0x1f,0x06,0x91,0x4e,0x07,0x86,0x70,0x2b,0xc0,0x7c,0x5e,0xb3,0xca,0xdc,0x1a,0x8b, +0x82,0xdd,0x41,0x8a,0x62,0x0f,0xba,0xd1,0xd7,0x80,0xc8,0x20,0x77,0xba,0xe7,0xe1, +0x36,0xf8,0x76,0x9a,0x54,0x6a,0x1b,0x67,0x45,0x3b,0xd7,0x85,0x84,0xbe,0x11,0xe6, +0x6c,0x70,0xd8,0x18,0x68,0xd8,0xa7,0xa3,0x53,0x30,0x51,0x30,0x1d,0x06,0x03,0x55, +0x1d,0x0e,0x04,0x16,0x04,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf,0x63,0xa4,0x12,0x29, +0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x1f,0x06,0x03,0x55,0x1d, +0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf,0x63,0xa4,0x12, +0x29,0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x0f,0x06,0x03,0x55, +0x1d,0x13,0x01,0x01,0xff,0x04,0x05,0x30,0x03,0x01,0x01,0xff,0x30,0x0a,0x06,0x08, +0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,0x48,0x00,0x30,0x45,0x02,0x21,0x00, +0xe6,0xb6,0x11,0x8d,0x75,0x3a,0x62,0xf3,0x08,0x17,0xce,0xa5,0x5a,0xcb,0x61,0xc7, +0x0a,0x33,0xdb,0x30,0x29,0x6b,0x5e,0xac,0xfc,0xaa,0xed,0x14,0xd1,0xd7,0xae,0x24, +0x02,0x20,0x2e,0x4d,0x70,0xc7,0x26,0xf7,0xea,0xa3,0x07,0x8a,0x6f,0x98,0x07,0xe1, +0xbc,0x38,0x13,0x88,0x17,0xdd,0x01,0x21,0x1e,0xb0,0xbb,0x32,0xfc,0x7a,0xc0,0xd5, +0x80,0x45,0x31,0x82,0x01,0x23,0x30,0x82,0x01,0x1f,0x02,0x01,0x01,0x30,0x55,0x30, +0x3d,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a, +0x30,0x08,0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03, +0x55,0x04,0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c, +0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x02,0x14, +0x13,0x09,0x38,0x76,0x3a,0x38,0xef,0x36,0xac,0xc3,0xa5,0x7e,0xa5,0xad,0x56,0x50, +0x8d,0x77,0x55,0x2c,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02, +0x01,0x05,0x00,0xa0,0x5e,0x30,0x10,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37, +0x02,0x01,0x0c,0x31,0x02,0x30,0x00,0x30,0x19,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7, +0x0d,0x01,0x09,0x03,0x31,0x0c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02, +0x01,0x04,0x30,0x2f,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x04,0x31, +0x22,0x04,0x20,0x25,0xc1,0x32,0xc0,0x4f,0x1a,0xae,0x84,0xd2,0x6a,0xff,0x0e,0xc9, +0xe8,0x85,0xbc,0x38,0x63,0x7b,0x22,0x89,0x1c,0x97,0x29,0xc2,0x8f,0x70,0x40,0xc2, +0xdf,0x42,0x9a,0x30,0x0b,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x05,0x00, +0x04,0x47,0x30,0x45,0x02,0x20,0x07,0x66,0x32,0x9a,0x15,0x8f,0x39,0x0a,0xb0,0xe1, +0x80,0xc9,0x82,0x23,0xb8,0x99,0x54,0x4c,0xa7,0x65,0xf2,0x99,0x11,0x70,0x1e,0xdf, +0xf5,0x40,0x73,0x7a,0x8d,0xd1,0x02,0x21,0x00,0x84,0xe0,0xec,0x38,0x33,0x01,0x28, +0x2b,0x4b,0x72,0xed,0x6a,0x64,0xb7,0xaf,0x7a,0x34,0x4b,0x6b,0x69,0xf6,0x55,0x9a, +0x8e,0x0d,0xe9,0xc1,0x85,0x80,0x4d,0xef,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; + +static void test_verify_ecc_signature(void) +{ + HCERTSTORE store; + HCRYPTKEY key; + BCRYPT_KEY_HANDLE bkey; + HCRYPTMSG msg; + BOOL bret; + CERT_INFO *cert_info; + PCCERT_CONTEXT cert; + DWORD size; + CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA verify_para = { sizeof(verify_para) }; + HCRYPTOIDFUNCSET set; + void *import_func; + HCRYPTOIDFUNCADDR hfunc = NULL; + CMSG_CMS_SIGNER_INFO *signer_info; + + msg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING, 0, 0, 0, NULL, NULL); + ok(!!msg, "failed, error %#lx.\n", GetLastError()); + bret = CryptMsgUpdate(msg, msg_signed_ecc_prime256v1, sizeof(msg_signed_ecc_prime256v1), TRUE); + ok(bret, "failed, error %#lx.\n", GetLastError()); + store = CertOpenStore(CERT_STORE_PROV_MSG, X509_ASN_ENCODING, 0, 0, msg); + ok(!!store, "failed, error %#lx.\n", GetLastError()); + size = 0; + bret = CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, 0, NULL, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + cert_info = malloc(size); + bret = CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, 0, cert_info, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + cert = CertGetSubjectCertificateFromStore(store, X509_ASN_ENCODING, cert_info); + ok(!!cert, "failed, error %#lx.\n", GetLastError()); + + ok(!strcmp(cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY), + "got OID %s.\n", cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId); + size = 0; + bret = CryptMsgGetParam(msg, CMSG_CMS_SIGNER_INFO_PARAM, 0, NULL, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + signer_info = malloc(size); + bret = CryptMsgGetParam(msg, CMSG_CMS_SIGNER_INFO_PARAM, 0, signer_info, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + ok(!strcmp(signer_info->HashAlgorithm.pszObjId, szOID_NIST_sha256), "got %s.\n", + signer_info->HashAlgorithm.pszObjId); + ok(!strcmp(signer_info->HashEncryptionAlgorithm.pszObjId, szOID_ECC_PUBLIC_KEY), "got %s.\n", + signer_info->HashEncryptionAlgorithm.pszObjId); + + set = CryptInitOIDFunctionSet(CRYPT_OID_IMPORT_PUBLIC_KEY_INFO_FUNC, 0); + ok(!!set, "failed, error %#lx.\n", GetLastError()); + bret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, + 0, (void **)&import_func, &hfunc); + ok(!bret, "succeeded.\n"); + + bret = CryptImportPublicKeyInfo(0, X509_ASN_ENCODING, &cert->pCertInfo->SubjectPublicKeyInfo, &key); + ok(!bret && GetLastError() == CRYPT_E_ASN1_BADTAG, "got ret %d, error %#lx.\n", bret, GetLastError()); + + bret = CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &cert->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &bkey); + ok(bret, "failed, error %#lx.\n", GetLastError()); + BCryptDestroyKey(bkey); + + bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE, cert->pCertInfo); + todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + + verify_para.dwSignerType = CMSG_VERIFY_SIGNER_CERT; + verify_para.pvSigner = (void *)cert; + bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE_EX, &verify_para); + todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + + free(signer_info); + free(cert_info); + CertFreeCertificateContext(cert); + CertCloseStore(store, 0); + CryptMsgClose(msg); +} + START_TEST(msg) { /* Basic parameter checking tests */ @@ -3500,4 +3639,5 @@ START_TEST(msg) test_decode_msg(); test_msg_get_and_verify_signer(); + test_verify_ecc_signature(); } From 0fcf7029f91ef20512da6160f8536ee29cf7c55a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 29 Jun 2023 11:12:28 -0600 Subject: [PATCH 584/758] crypt32: Add OID info for szOID_ECC_PUBLIC_KEY. CW-Bug-Id: #22376 --- dlls/crypt32/oid.c | 2 ++ dlls/crypt32/tests/oid.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dlls/crypt32/oid.c b/dlls/crypt32/oid.c index a4dcc3997f0..70b9e638ab7 100644 --- a/dlls/crypt32/oid.c +++ b/dlls/crypt32/oid.c @@ -1271,6 +1271,8 @@ static const struct OIDInfoConstructor { { 3, szOID_INFOSEC_mosaicKMandUpdSig, CALG_DSS_SIGN, L"mosaicKMandUpdSig", &mosaicFlagsBlob }, { 3, szOID_RSA_SMIMEalgESDH, CALG_DH_EPHEM, L"ESDH", &noNullBlob }, { 3, szOID_PKIX_NO_SIGNATURE, CALG_NO_SIGN, L"NOSIGN", NULL }, + { 3, szOID_ECC_PUBLIC_KEY, CALG_OID_INFO_PARAMETERS, L"ECC", NULL, + CRYPT_OID_INFO_ECC_PARAMETERS_ALGORITHM, L"" }, { 4, szOID_RSA_SHA1RSA, CALG_SHA1, L"sha1RSA", &rsaSignBlob }, { 4, szOID_RSA_SHA256RSA, CALG_SHA_256, L"sha256RSA", &rsaSignBlob }, diff --git a/dlls/crypt32/tests/oid.c b/dlls/crypt32/tests/oid.c index 715d76b9c31..2423c819229 100644 --- a/dlls/crypt32/tests/oid.c +++ b/dlls/crypt32/tests/oid.c @@ -72,7 +72,8 @@ static const struct OIDToAlgID oidToAlgID[] = { { szOID_INFOSEC_mosaicKMandUpdSig, CALG_DSS_SIGN }, { szOID_NIST_sha256, CALG_SHA_256, -1 }, { szOID_NIST_sha384, CALG_SHA_384, -1 }, - { szOID_NIST_sha512, CALG_SHA_512, -1 } + { szOID_NIST_sha512, CALG_SHA_512, -1 }, + { szOID_ECC_PUBLIC_KEY, CALG_OID_INFO_PARAMETERS }, }; static const struct OIDToAlgID algIDToOID[] = { @@ -509,6 +510,7 @@ static void test_findOIDInfo(void) { static CHAR oid_rsa_md5[] = szOID_RSA_MD5, oid_sha256[] = szOID_NIST_sha256; static CHAR oid_ecdsa_sha256[] = szOID_ECDSA_SHA256; + static CHAR oid_ecc_public_key[] = szOID_ECC_PUBLIC_KEY; ALG_ID alg = CALG_SHA1; ALG_ID algs[2] = { CALG_MD5, CALG_RSA_SIGN }; const struct oid_info @@ -573,6 +575,17 @@ static void test_findOIDInfo(void) } else win_skip("Host does not support ECDSA_SHA256, skipping test\n"); + + info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid_ecc_public_key, 0); + ok(!!info, "got error %#lx.\n", GetLastError()); + ok(!strcmp(info->pszOID, oid_ecc_public_key), "got %s.\n", info->pszOID); + ok(!wcscmp(info->pwszName, L"ECC"), "got %s.\n", wine_dbgstr_w(info->pwszName)); + ok(info->dwGroupId == CRYPT_PUBKEY_ALG_OID_GROUP_ID, "got %lu.\n", info->dwGroupId); + ok(U(*info).Algid == CALG_OID_INFO_PARAMETERS, "got %d.\n", U(*info).Algid); + ok(!info->ExtraInfo.cbData, "got %ld.\n", info->ExtraInfo.cbData); + ok(!wcscmp(info->pwszCNGAlgid, CRYPT_OID_INFO_ECC_PARAMETERS_ALGORITHM), "got %s.\n", wine_dbgstr_w(info->pwszCNGAlgid)); + ok(info->pwszCNGExtraAlgid && !wcscmp(info->pwszCNGExtraAlgid, L""), "got %s.\n", + wine_dbgstr_w(info->pwszCNGExtraAlgid)); } static void test_registerOIDInfo(void) From 7b04d7a75b026470305fc8acf07de80dcad03c12 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 29 Jun 2023 11:45:00 -0600 Subject: [PATCH 585/758] crypt32: Factor out extract_hash() helper. CW-Bug-Id: #22376 --- dlls/crypt32/msg.c | 63 +++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 63884d0c275..de5730bc064 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -43,6 +43,24 @@ typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData, typedef BOOL (*CryptMsgControlFunc)(HCRYPTMSG hCryptMsg, DWORD dwFlags, DWORD dwCtrlType, const void *pvCtrlPara); +static BOOL extract_hash(HCRYPTHASH hash, BYTE **data, DWORD *size) +{ + DWORD sz; + + *data = NULL; + sz = sizeof(*size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *)size, &sz, 0)) return FALSE; + if (!(*data = CryptMemAlloc(*size))) + { + ERR("No memory.\n"); + return FALSE; + } + if (CryptGetHashParam(hash, HP_HASHVAL, *data, size, 0)) return TRUE; + CryptMemFree(*data); + *data = NULL; + return FALSE; +} + static BOOL CRYPT_DefaultMsgControl(HCRYPTMSG hCryptMsg, DWORD dwFlags, DWORD dwCtrlType, const void *pvCtrlPara) { @@ -412,18 +430,7 @@ static BOOL CRYPT_EncodePKCSDigestedData(CHashEncodeMsg *msg, void *pvData, &digestedData.ContentInfo.Content.cbData); } if (msg->base.state == MsgStateFinalized) - { - size = sizeof(DWORD); - ret = CryptGetHashParam(msg->hash, HP_HASHSIZE, - (LPBYTE)&digestedData.hash.cbData, &size, 0); - if (ret) - { - digestedData.hash.pbData = CryptMemAlloc( - digestedData.hash.cbData); - ret = CryptGetHashParam(msg->hash, HP_HASHVAL, - digestedData.hash.pbData, &digestedData.hash.cbData, 0); - } - } + ret = extract_hash(msg->hash, &digestedData.hash.pbData, &digestedData.hash.cbData); if (ret) ret = CRYPT_AsnEncodePKCSDigestedData(&digestedData, pvData, pcbData); @@ -1025,35 +1032,23 @@ static BOOL CSignedMsgData_AppendMessageDigestAttribute( CSignedMsgData *msg_data, DWORD signerIndex) { BOOL ret; - DWORD size; CRYPT_HASH_BLOB hash = { 0, NULL }, encodedHash = { 0, NULL }; char messageDigest[] = szOID_RSA_messageDigest; CRYPT_ATTRIBUTE messageDigestAttr = { messageDigest, 1, &encodedHash }; - size = sizeof(DWORD); - ret = CryptGetHashParam( - msg_data->signerHandles[signerIndex].contentHash, HP_HASHSIZE, - (LPBYTE)&hash.cbData, &size, 0); + if (!(ret = extract_hash(msg_data->signerHandles[signerIndex].contentHash, &hash.pbData, &hash.cbData))) + return FALSE; + + ret = CRYPT_AsnEncodeOctets(0, NULL, &hash, CRYPT_ENCODE_ALLOC_FLAG, NULL, (LPBYTE)&encodedHash.pbData, + &encodedHash.cbData); if (ret) { - hash.pbData = CryptMemAlloc(hash.cbData); - ret = CryptGetHashParam( - msg_data->signerHandles[signerIndex].contentHash, HP_HASHVAL, - hash.pbData, &hash.cbData, 0); - if (ret) - { - ret = CRYPT_AsnEncodeOctets(0, NULL, &hash, CRYPT_ENCODE_ALLOC_FLAG, - NULL, (LPBYTE)&encodedHash.pbData, &encodedHash.cbData); - if (ret) - { - ret = CRYPT_AppendAttribute( - &msg_data->info->rgSignerInfo[signerIndex].AuthAttrs, - &messageDigestAttr); - LocalFree(encodedHash.pbData); - } - } - CryptMemFree(hash.pbData); + ret = CRYPT_AppendAttribute( + &msg_data->info->rgSignerInfo[signerIndex].AuthAttrs, + &messageDigestAttr); + LocalFree(encodedHash.pbData); } + CryptMemFree(hash.pbData); return ret; } From a6a0ffeb751d23164619671d9558d678d77108b0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 29 Jun 2023 11:53:28 -0600 Subject: [PATCH 586/758] crypt32: Factor out cng_prepare_signature(). CW-Bug-Id: #22376 --- dlls/crypt32/cert.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index b9645770ce1..0005d5db14a 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -2810,7 +2810,18 @@ static BOOL CNG_PrepareSignatureECC(BYTE *encoded_sig, DWORD encoded_size, BYTE return TRUE; } -static BOOL CNG_PrepareSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, +static BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, + BYTE **sig_value, DWORD *sig_len) +{ + if (!strcmp(alg_oid, szOID_ECC_PUBLIC_KEY)) + return CNG_PrepareSignatureECC(encoded_sig, encoded_sig_len, sig_value, sig_len); + + FIXME("Unsupported public key type: %s\n", debugstr_a(alg_oid)); + SetLastError(NTE_BAD_ALGID); + return FALSE; +} + +static BOOL CNG_PrepareCertSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, BYTE **sig_value, DWORD *sig_len) { BYTE *encoded_sig; @@ -2832,14 +2843,8 @@ static BOOL CNG_PrepareSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SI for (i = 0; i < signedCert->Signature.cbData; i++) encoded_sig[i] = signedCert->Signature.pbData[signedCert->Signature.cbData - i - 1]; - if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY)) - ret = CNG_PrepareSignatureECC(encoded_sig, signedCert->Signature.cbData, sig_value, sig_len); - else - { - FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId)); - SetLastError(NTE_BAD_ALGID); - } - + ret = cng_prepare_signature(pubKeyInfo->Algorithm.pszObjId, encoded_sig, signedCert->Signature.cbData, + sig_value, sig_len); CryptMemFree(encoded_sig); return ret; } @@ -2859,7 +2864,7 @@ static BOOL CNG_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodi ret = CNG_CalcHash(info->pwszCNGAlgid, signedCert, &hash_value, &hash_len); if (ret) { - ret = CNG_PrepareSignature(pubKeyInfo, signedCert, &sig_value, &sig_len); + ret = CNG_PrepareCertSignature(pubKeyInfo, signedCert, &sig_value, &sig_len); if (ret) { status = BCryptVerifySignature(key, NULL, hash_value, hash_len, sig_value, sig_len, 0); From c298362cf7d2819f07e4f95be2799f540c53d47e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 29 Jun 2023 12:39:33 -0600 Subject: [PATCH 587/758] crypt32: Support CNG keys in CDecodeSignedMsg_VerifySignatureWithKey(). CW-Bug-Id: #22376 --- dlls/crypt32/cert.c | 2 +- dlls/crypt32/crypt32_private.h | 2 ++ dlls/crypt32/msg.c | 42 ++++++++++++++++++++++++++++++---- dlls/crypt32/tests/msg.c | 4 ++-- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 0005d5db14a..a6fefb30b05 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -2810,7 +2810,7 @@ static BOOL CNG_PrepareSignatureECC(BYTE *encoded_sig, DWORD encoded_size, BYTE return TRUE; } -static BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, +BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, BYTE **sig_value, DWORD *sig_len) { if (!strcmp(alg_oid, szOID_ECC_PUBLIC_KEY)) diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h index e29249b1136..7d0172f6a29 100644 --- a/dlls/crypt32/crypt32_private.h +++ b/dlls/crypt32/crypt32_private.h @@ -23,6 +23,8 @@ #include "wine/unixlib.h" BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key) DECLSPEC_HIDDEN; +BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, + BYTE **sig_value, DWORD *sig_len) DECLSPEC_HIDDEN; /* a few asn.1 tags we need */ #define ASN_BOOL (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x01) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index de5730bc064..94642ecfce2 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -3316,24 +3316,56 @@ static BOOL CDecodeHashMsg_VerifyHash(CDecodeMsg *msg) return ret; } +static BOOL cng_verify_msg_signature(CMSG_CMS_SIGNER_INFO *signer, HCRYPTHASH hash, CERT_PUBLIC_KEY_INFO *key_info) +{ + BYTE *hash_value, *sig_value = NULL; + DWORD hash_len, sig_len; + BCRYPT_KEY_HANDLE key; + BOOL ret = FALSE; + NTSTATUS status; + + if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, key_info, 0, NULL, &key)) return FALSE; + if (!extract_hash(hash, &hash_value, &hash_len)) goto done; + if (!cng_prepare_signature(key_info->Algorithm.pszObjId, signer->EncryptedHash.pbData, + signer->EncryptedHash.cbData, &sig_value, &sig_len)) goto done; + status = BCryptVerifySignature(key, NULL, hash_value, hash_len, sig_value, sig_len, 0); + if (status) + { + FIXME("Failed to verify signature: %08lx.\n", status); + SetLastError(RtlNtStatusToDosError(status)); + } + ret = !status; +done: + CryptMemFree(sig_value); + CryptMemFree(hash_value); + BCryptDestroyKey(key); + return ret; +} + static BOOL CDecodeSignedMsg_VerifySignatureWithKey(CDecodeMsg *msg, HCRYPTPROV prov, DWORD signerIndex, PCERT_PUBLIC_KEY_INFO keyInfo) { + HCRYPTHASH hash; HCRYPTKEY key; BOOL ret; + ALG_ID alg_id = 0; + + if (msg->u.signed_data.info->rgSignerInfo[signerIndex].AuthAttrs.cAttr) + hash = msg->u.signed_data.signerHandles[signerIndex].authAttrHash; + else + hash = msg->u.signed_data.signerHandles[signerIndex].contentHash; + + if (keyInfo->Algorithm.pszObjId) alg_id = CertOIDToAlgId(keyInfo->Algorithm.pszObjId); + if (alg_id == CALG_OID_INFO_PARAMETERS || alg_id == CALG_OID_INFO_CNG_ONLY) + return cng_verify_msg_signature(&msg->u.signed_data.info->rgSignerInfo[signerIndex], hash, keyInfo); if (!prov) prov = msg->crypt_prov; ret = CryptImportPublicKeyInfo(prov, X509_ASN_ENCODING, keyInfo, &key); if (ret) { - HCRYPTHASH hash; CRYPT_HASH_BLOB reversedHash; - if (msg->u.signed_data.info->rgSignerInfo[signerIndex].AuthAttrs.cAttr) - hash = msg->u.signed_data.signerHandles[signerIndex].authAttrHash; - else - hash = msg->u.signed_data.signerHandles[signerIndex].contentHash; ret = CRYPT_ConstructBlob(&reversedHash, &msg->u.signed_data.info->rgSignerInfo[signerIndex].EncryptedHash); if (ret) diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index c663909ed7c..d8ac7f75d78 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -3608,12 +3608,12 @@ static void test_verify_ecc_signature(void) BCryptDestroyKey(bkey); bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE, cert->pCertInfo); - todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + ok(bret, "failed, error %#lx.\n", GetLastError()); verify_para.dwSignerType = CMSG_VERIFY_SIGNER_CERT; verify_para.pvSigner = (void *)cert; bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE_EX, &verify_para); - todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + ok(bret, "failed, error %#lx.\n", GetLastError()); free(signer_info); free(cert_info); From bf69f33a94cde3a09bbbd675af5402e5dc999cb7 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 588/758] windows.media.speech: Adding a couple of synthesizer's options tests. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index be616e34d62..01ad5ebe6fe 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1048,16 +1048,54 @@ static void test_SpeechSynthesizer(void) if (hr == S_OK) { + ISpeechSynthesizerOptions2 *options2; ISpeechSynthesizerOptions3 *options3; + boolean bool_value; + DOUBLE double_value; + enum SpeechAppendedSilence silence_value; + enum SpeechPunctuationSilence punctuation_value; check_interface(options, &IID_IAgileObject, TRUE); + bool_value = 0xff; + hr = ISpeechSynthesizerOptions_get_IncludeSentenceBoundaryMetadata(options, &bool_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(! bool_value, "Got unepected option %u\n", bool_value); + bool_value = 0xff; + hr = ISpeechSynthesizerOptions_get_IncludeWordBoundaryMetadata(options, &bool_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!bool_value, "Got unepected option %u\n", bool_value); + check_optional_interface(options, &IID_ISpeechSynthesizerOptions2, TRUE); /* Requires Win10 >= 1709 */ + hr = ISpeechSynthesizerOptions_QueryInterface(options, &IID_ISpeechSynthesizerOptions2, (void **)&options2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ISpeechSynthesizerOptions2_get_AudioPitch(options2, &double_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(double_value == 1.0f, "Got unepected option %f\n", double_value); + + hr = ISpeechSynthesizerOptions2_get_AudioVolume(options2, &double_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(double_value == 1.0f, "Got unepected option %f\n", double_value); + + hr = ISpeechSynthesizerOptions2_get_SpeakingRate(options2, &double_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(double_value == 1.0f, "Got unepected option %f\n", double_value); + + ISpeechSynthesizerOptions2_Release(options2); hr = ISpeechSynthesizerOptions_QueryInterface(options, &IID_ISpeechSynthesizerOptions3, (void **)&options3); ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Got unexpected hr %#lx.\n", hr); /* Requires Win10 >= 1803 */ if (hr == S_OK) { + hr = ISpeechSynthesizerOptions3_get_AppendedSilence(options3, &silence_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(silence_value == SpeechAppendedSilence_Default, "Got unepected option %u\n", silence_value); + + hr = ISpeechSynthesizerOptions3_get_PunctuationSilence(options3, &punctuation_value); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(punctuation_value == SpeechPunctuationSilence_Default, "Got unepected option %u\n", punctuation_value); + ref = ISpeechSynthesizerOptions3_Release(options3); ok(ref == 2, "Got unexpected ref %lu.\n", ref); } From b6f81d924de13689e9c67398da28822b83e316c8 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 589/758] windows.media.speech: Add basic implementation on synthesizer options. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/synthesizer.c | 326 ++++++++++++++++++++++- dlls/windows.media.speech/tests/speech.c | 2 +- 2 files changed, 325 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 39d14b84ab7..95dfe3150bb 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -280,6 +280,309 @@ static HRESULT synthesis_stream_create( ISpeechSynthesisStream **out ) return hr; } +/* + * + * SpeechSynthesizerOptions runtimeclass + * + */ +struct synthesizer_options +{ + ISpeechSynthesizerOptions ISpeechSynthesizerOptions_iface; + ISpeechSynthesizerOptions2 ISpeechSynthesizerOptions2_iface; + ISpeechSynthesizerOptions3 ISpeechSynthesizerOptions3_iface; + LONG ref; + + /* options */ + boolean include_word_boundary; + boolean include_sentence_boundary; + + /* options 2 */ + double audio_volume; + double speaking_rate; + double audio_pitch; + + /* options 3 */ + enum SpeechAppendedSilence appended_silence; + enum SpeechPunctuationSilence punctuation_silence; +}; + +static inline struct synthesizer_options *impl_from_ISpeechSynthesizerOptions( ISpeechSynthesizerOptions *iface ) +{ + return CONTAINING_RECORD(iface, struct synthesizer_options, ISpeechSynthesizerOptions_iface); +} + +static HRESULT WINAPI synthesizer_options_QueryInterface( ISpeechSynthesizerOptions *iface, REFIID iid, void **out) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + + TRACE("iface %p, iid %s, out %p stub!\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_ISpeechSynthesizerOptions)) + { + IInspectable_AddRef((*out = &impl->ISpeechSynthesizerOptions_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechSynthesizerOptions2)) + { + IInspectable_AddRef((*out = &impl->ISpeechSynthesizerOptions2_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechSynthesizerOptions3)) + { + IInspectable_AddRef((*out = &impl->ISpeechSynthesizerOptions3_iface)); + return S_OK; + } + + FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI synthesizer_options_AddRef( ISpeechSynthesizerOptions *iface ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI synthesizer_options_Release( ISpeechSynthesizerOptions *iface ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + TRACE("iface %p, ref %lu.\n", iface, ref); + if (ref == 0) + free(impl); + return ref; +} + +static HRESULT WINAPI synthesizer_options_GetIids( ISpeechSynthesizerOptions *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 synthesizer_options_GetRuntimeClassName( ISpeechSynthesizerOptions *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub.\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI synthesizer_options_GetTrustLevel( ISpeechSynthesizerOptions *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub.\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI synthesizer_options_get_IncludeWordBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean *value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %p semi-stub.\n", iface, value); + + *value = impl->include_word_boundary; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options_put_IncludeWordBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %s semi-stub.\n", iface, value ? "true" : "false"); + + impl->include_word_boundary = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options_get_IncludeSentenceBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean *value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %p stub.\n", iface, value); + + *value = impl->include_sentence_boundary; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options_put_IncludeSentenceBoundaryMetadata( ISpeechSynthesizerOptions *iface, boolean value ) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions(iface); + TRACE("iface %p, value %s stub.\n", iface, value ? "true" : "false"); + + impl->include_sentence_boundary = value; + return S_OK; +} + +static const struct ISpeechSynthesizerOptionsVtbl synthesizer_options_vtbl = +{ + /*** IUnknown methods ***/ + synthesizer_options_QueryInterface, + synthesizer_options_AddRef, + synthesizer_options_Release, + /*** IInspectable methods ***/ + synthesizer_options_GetIids, + synthesizer_options_GetRuntimeClassName, + synthesizer_options_GetTrustLevel, + /*** ISpeechSynthesizerOptions methods ***/ + synthesizer_options_get_IncludeWordBoundaryMetadata, + synthesizer_options_put_IncludeWordBoundaryMetadata, + synthesizer_options_get_IncludeSentenceBoundaryMetadata, + synthesizer_options_put_IncludeSentenceBoundaryMetadata, +}; + +DEFINE_IINSPECTABLE(synthesizer_options2, ISpeechSynthesizerOptions2, struct synthesizer_options, ISpeechSynthesizerOptions_iface) + +static HRESULT WINAPI synthesizer_options2_get_AudioVolume( ISpeechSynthesizerOptions2 *iface, DOUBLE *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->audio_volume; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_put_AudioVolume( ISpeechSynthesizerOptions2 *iface, DOUBLE value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %g semi-stub!\n", iface, value); + impl->audio_volume = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_get_SpeakingRate( ISpeechSynthesizerOptions2 *iface, DOUBLE *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->speaking_rate; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_put_SpeakingRate( ISpeechSynthesizerOptions2 *iface, DOUBLE value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %g semi-stub!\n", iface, value); + impl->speaking_rate = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_get_AudioPitch( ISpeechSynthesizerOptions2 *iface, DOUBLE *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->audio_pitch; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options2_put_AudioPitch( ISpeechSynthesizerOptions2 *iface, DOUBLE value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions2(iface); + + TRACE("iface %p value %g semi-stub!\n", iface, value); + impl->audio_pitch = value; + return S_OK; +} + +static const struct ISpeechSynthesizerOptions2Vtbl synthesizer_options2_vtbl = +{ + /*** IUnknown methods ***/ + synthesizer_options2_QueryInterface, + synthesizer_options2_AddRef, + synthesizer_options2_Release, + /*** IInspectable methods ***/ + synthesizer_options2_GetIids, + synthesizer_options2_GetRuntimeClassName, + synthesizer_options2_GetTrustLevel, + /*** ISpeechSynthesizerOptions methods ***/ + synthesizer_options2_get_AudioVolume, + synthesizer_options2_put_AudioVolume, + synthesizer_options2_get_SpeakingRate, + synthesizer_options2_put_SpeakingRate, + synthesizer_options2_get_AudioPitch, + synthesizer_options2_put_AudioPitch, +}; + +DEFINE_IINSPECTABLE(synthesizer_options3, ISpeechSynthesizerOptions3, struct synthesizer_options, ISpeechSynthesizerOptions_iface) + +static HRESULT WINAPI synthesizer_options3_get_AppendedSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechAppendedSilence *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->appended_silence; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options3_put_AppendedSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechAppendedSilence value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %u semi-stub!\n", iface, value); + impl->appended_silence = value; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options3_get_PunctuationSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechPunctuationSilence *value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %p semi-stub!\n", iface, value); + *value = impl->punctuation_silence; + return S_OK; +} + +static HRESULT WINAPI synthesizer_options3_put_PunctuationSilence( ISpeechSynthesizerOptions3 *iface, enum SpeechPunctuationSilence value) +{ + struct synthesizer_options *impl = impl_from_ISpeechSynthesizerOptions3(iface); + + TRACE("iface %p value %u semi-stub!\n", iface, value); + impl->punctuation_silence = value; + return S_OK; +} + +static const struct ISpeechSynthesizerOptions3Vtbl synthesizer_options3_vtbl = +{ + /*** IUnknown methods ***/ + synthesizer_options3_QueryInterface, + synthesizer_options3_AddRef, + synthesizer_options3_Release, + /*** IInspectable methods ***/ + synthesizer_options3_GetIids, + synthesizer_options3_GetRuntimeClassName, + synthesizer_options3_GetTrustLevel, + /*** ISpeechSynthesizerOptions methods ***/ + synthesizer_options3_get_AppendedSilence, + synthesizer_options3_put_AppendedSilence, + synthesizer_options3_get_PunctuationSilence, + synthesizer_options3_put_PunctuationSilence, +}; + +static HRESULT synthesizer_options_allocate( struct synthesizer_options **out ) +{ + struct synthesizer_options *options; + + if (!(options = calloc(1, sizeof(*options)))) return E_OUTOFMEMORY; + + options->ISpeechSynthesizerOptions_iface.lpVtbl = &synthesizer_options_vtbl; + options->ISpeechSynthesizerOptions2_iface.lpVtbl = &synthesizer_options2_vtbl; + options->ISpeechSynthesizerOptions3_iface.lpVtbl = &synthesizer_options3_vtbl; + /* all other values default to 0 or false */ + options->audio_pitch = 1.0; + options->audio_volume = 1.0; + options->speaking_rate = 1.0; + options->ref = 1; + *out = options; + + return S_OK; +} + /* * * SpeechSynthesizer runtimeclass @@ -292,6 +595,8 @@ struct synthesizer ISpeechSynthesizer2 ISpeechSynthesizer2_iface; IClosable IClosable_iface; LONG ref; + + struct synthesizer_options *options; }; /* @@ -352,7 +657,11 @@ static ULONG WINAPI synthesizer_Release( ISpeechSynthesizer *iface ) TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) + { + if (impl->options) + ISpeechSynthesizerOptions_Release(&impl->options->ISpeechSynthesizerOptions_iface); free(impl); + } return ref; } @@ -440,8 +749,21 @@ DEFINE_IINSPECTABLE(synthesizer2, ISpeechSynthesizer2, struct synthesizer, ISpee static HRESULT WINAPI synthesizer2_get_Options( ISpeechSynthesizer2 *iface, ISpeechSynthesizerOptions **value ) { - FIXME("iface %p, value %p stub.\n", iface, value); - return E_NOTIMPL; + struct synthesizer *impl = impl_from_ISpeechSynthesizer2(iface); + + WARN("iface %p, value %p semi-stub.\n", iface, value); + if (!impl->options) + { + struct synthesizer_options *options; + HRESULT hr = synthesizer_options_allocate(&options); + if (FAILED(hr)) return hr; + + if (InterlockedCompareExchangePointer((void **)&impl->options, options, NULL) != NULL) + /* another thread beat us */ + ISpeechSynthesizerOptions_AddRef(&options->ISpeechSynthesizerOptions_iface); + } + ISpeechSynthesizerOptions_AddRef(*value = &impl->options->ISpeechSynthesizerOptions_iface); + return S_OK; } static const struct ISpeechSynthesizer2Vtbl synthesizer2_vtbl = diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 01ad5ebe6fe..49c3daf2dd0 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1044,7 +1044,7 @@ static void test_SpeechSynthesizer(void) ISpeechSynthesizerOptions *options; hr = ISpeechSynthesizer2_get_Options(synthesizer2, &options); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); if (hr == S_OK) { From e1a0f0782c0484cf8a101cbc79701a6d29cfc38d Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 590/758] windows.media.speech: Add more tests about IVoiceInformation. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 73 +++++++++++++++++++++++- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 49c3daf2dd0..9dad84f778b 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -698,6 +698,66 @@ static HRESULT WINAPI iterable_hstring_create_static( struct iterable_hstring *i return S_OK; } +#define check_comparable_presence(a, b) _check_comparable_presence( __LINE__, (a), (b)) +static void _check_comparable_presence( unsigned line, IVectorView_VoiceInformation *voices, IVoiceInformation *voice) +{ + HSTRING in_display, in_id, in_language; + HSTRING vc_display, vc_id, vc_language; + IVoiceInformation *vc_voice; + enum VoiceGender in_gender, vc_gender; + UINT32 size, idx, found_count = 0; + HRESULT hr; + INT32 cmp; + + hr = IVoiceInformation_get_DisplayName(voice, &in_display); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(voice, &in_id); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Language(voice, &in_language); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Gender(voice, &in_gender); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVectorView_VoiceInformation_get_Size(voices, &size); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + for (idx = 0; SUCCEEDED(hr = IVectorView_VoiceInformation_GetAt(voices, idx, &vc_voice)); idx++) + { + hr = IVoiceInformation_get_DisplayName(vc_voice, &vc_display); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(vc_voice, &vc_id); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Language(vc_voice, &vc_language); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Gender(vc_voice, &vc_gender); + ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + trace("%u] %s/%s/%s/%u\n", + idx - 1, debugstr_hstring(vc_display), debugstr_hstring(vc_id), debugstr_hstring(vc_language), vc_gender); + + if (SUCCEEDED(WindowsCompareStringOrdinal(in_display, vc_display, &cmp)) && !cmp && + SUCCEEDED(WindowsCompareStringOrdinal(in_id, vc_id, &cmp)) && !cmp && + SUCCEEDED(WindowsCompareStringOrdinal(in_language, vc_language, &cmp)) && !cmp && + in_gender == vc_gender) + { + found_count++; + } + WindowsDeleteString(vc_display); + WindowsDeleteString(vc_id); + WindowsDeleteString(vc_language); + IVoiceInformation_Release(vc_voice); + } + ok(hr == E_BOUNDS, "Got unexpected hr %#lx.\n", hr); + ok(idx != 0, "Vector view shouldn't be empty!\n"); + ok(idx == size, "Incoherent index/size %u/%u!\n", idx, size); + + ok_(__FILE__, line)(found_count == 1, "Found several (%u) instances of %s/%s/%s/%u\n", + found_count, + debugstr_hstring(in_display), debugstr_hstring(in_id), debugstr_hstring(in_language), in_gender); + + WindowsDeleteString(in_display); + WindowsDeleteString(in_id); + WindowsDeleteString(in_language); +} + static void test_ActivationFactory(void) { static const WCHAR *synthesizer_name = L"Windows.Media.SpeechSynthesis.SpeechSynthesizer"; @@ -822,7 +882,8 @@ static void test_SpeechSynthesizer(void) HMODULE hdll; HSTRING str, str2; HRESULT hr; - UINT32 size; + UINT32 size, idx; + BOOLEAN found; ULONG ref; hr = RoInitialize(RO_INIT_MULTITHREADED); @@ -916,13 +977,19 @@ static void test_SpeechSynthesizer(void) ok(hr == S_OK, "IVectorView_VoiceInformation_GetMany failed, hr %#lx\n", hr); ok(size == 0, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); - IVectorView_VoiceInformation_Release(voices); - hr = IInstalledVoicesStatic_get_DefaultVoice(voices_static, &voice); todo_wine ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); if (hr == S_OK) { + /* check that VoiceInformation in static vector voice are not shared when exposed to user */ + idx = size; + hr = IVectorView_VoiceInformation_IndexOf(voices, voice, &idx, &found); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!found, "Shouldn't find default element\n"); + + check_comparable_presence(voices, voice); + IVoiceInformation_get_Description(voice, &str2); trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); From 520f834f2cc5a8f447b77092350a90e20c1c7b19 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 591/758] windows.media.speech: Add basic implementation of IVoiceInformation. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/synthesizer.c | 181 ++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 95dfe3150bb..f49e06dbd4c 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -23,6 +23,187 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct voice_information +{ + IVoiceInformation IVoiceInformation_iface; + LONG ref; + + HSTRING id; + HSTRING display_name; + HSTRING language; + HSTRING description; + VoiceGender gender; +}; + +static inline struct voice_information *impl_from_IVoiceInformation( IVoiceInformation *iface ) +{ + return CONTAINING_RECORD(iface, struct voice_information, IVoiceInformation_iface); +} + +static void voice_information_delete( struct voice_information *voice_info ) +{ + WindowsDeleteString(voice_info->id); + WindowsDeleteString(voice_info->display_name); + WindowsDeleteString(voice_info->language); + WindowsDeleteString(voice_info->description); + free(voice_info); +} + +static HRESULT WINAPI voice_information_QueryInterface( IVoiceInformation *iface, REFIID iid, void **ppvObject) +{ + struct voice_information *impl = impl_from_IVoiceInformation( iface ); + + TRACE("iface %p, riid %s, ppv %p\n", iface, wine_dbgstr_guid(iid), ppvObject); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IVoiceInformation)) + { + IInspectable_AddRef((*ppvObject = &impl->IVoiceInformation_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *ppvObject = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI voice_information_AddRef( IVoiceInformation *iface ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI voice_information_Release( IVoiceInformation *iface ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + /* all voices are (for now) statically allocated in all_voices vector. so don't free them */ + return ref; +} + +static HRESULT WINAPI voice_information_GetIids( IVoiceInformation *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 voice_information_GetRuntimeClassName( IVoiceInformation *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI voice_information_GetTrustLevel( IVoiceInformation *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI voice_information_get_DisplayName( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p!n", iface, value); + return WindowsDuplicateString(impl->display_name, value); +} + +static HRESULT WINAPI voice_information_get_Id( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + return WindowsDuplicateString(impl->id, value); +} + +static HRESULT WINAPI voice_information_get_Language( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + return WindowsDuplicateString(impl->language, value); +} + +static HRESULT WINAPI voice_information_get_Description( IVoiceInformation *iface, HSTRING *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + return WindowsDuplicateString(impl->description, value); +} + +static HRESULT WINAPI voice_information_get_Gender( IVoiceInformation *iface, VoiceGender *value ) +{ + struct voice_information *impl = impl_from_IVoiceInformation(iface); + + TRACE("iface %p, value %p\n", iface, value); + *value = impl->gender; + return S_OK; +} + +static const struct IVoiceInformationVtbl voice_information_vtbl = +{ + /*** IUnknown methods ***/ + voice_information_QueryInterface, + voice_information_AddRef, + voice_information_Release, + + /*** IInspectable methods ***/ + voice_information_GetIids, + voice_information_GetRuntimeClassName, + voice_information_GetTrustLevel, + + /*** IVoiceInformation methods ***/ + voice_information_get_DisplayName, + voice_information_get_Id, + voice_information_get_Language, + voice_information_get_Description, + voice_information_get_Gender, +}; + +HRESULT voice_information_allocate(const WCHAR *display_name, const WCHAR *id, const WCHAR *locale, + VoiceGender gender, IVoiceInformation **pvoice) +{ + struct voice_information *voice_info; + WCHAR *description; + HRESULT hr; + size_t len, langlen; + + voice_info = calloc(1, sizeof(*voice_info)); + if (!voice_info) return E_OUTOFMEMORY; + + len = wcslen(display_name) + 3; + langlen = GetLocaleInfoEx(locale, LOCALE_SLOCALIZEDDISPLAYNAME, NULL, 0); + description = malloc((len + langlen) * sizeof(WCHAR)); + wcscpy(description, display_name); + wcscat(description, L" - "); + GetLocaleInfoEx(locale, LOCALE_SLOCALIZEDDISPLAYNAME, description + len, langlen); + + hr = WindowsCreateString(display_name, wcslen(display_name), &voice_info->display_name); + if (SUCCEEDED(hr)) + hr = WindowsCreateString(id, wcslen(id), &voice_info->id); + if (SUCCEEDED(hr)) + hr = WindowsCreateString(locale, wcslen(locale), &voice_info->language); + if (SUCCEEDED(hr)) + hr = WindowsCreateString(description, len + langlen - 1, &voice_info->description); + if (SUCCEEDED(hr)) + { + voice_info->gender = gender; + voice_info->IVoiceInformation_iface.lpVtbl = &voice_information_vtbl; + + *pvoice = &voice_info->IVoiceInformation_iface; + } + else + { + voice_information_delete(voice_info); + } + free(description); + return hr; +} + /* * * IVectorView_VoiceInformation From ff1b6d3a54c1a2b4bee15e016c0a0ed8ecdedb98 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 592/758] windows.media.speech: Finish implement the voice information view. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/private.h | 10 +++ dlls/windows.media.speech/synthesizer.c | 95 ++++++++++++++++++++---- dlls/windows.media.speech/tests/speech.c | 2 +- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 62952478bdf..60d09c9f7d1 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -133,4 +133,14 @@ HRESULT vector_inspectable_create( const struct vector_iids *iids, IVector_IInsp #define DEFINE_IINSPECTABLE_OUTER( pfx, iface_type, impl_type, outer_iface ) \ DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, impl->outer_iface ) +struct synth_provider +{ + struct IVoiceInformation **voices; + unsigned num_voices; + void (*dispose)(struct synth_provider *provider); +}; + +HRESULT voice_information_allocate(const WCHAR *display_name, const WCHAR *id, const WCHAR *locale, + VoiceGender gender, IVoiceInformation **pvoice); + #endif diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index f49e06dbd4c..02e3e76e183 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -214,6 +214,8 @@ struct voice_information_vector { IVectorView_VoiceInformation IVectorView_VoiceInformation_iface; LONG ref; + + struct synth_provider provider; }; static inline struct voice_information_vector *impl_from_IVectorView_VoiceInformation( IVectorView_VoiceInformation *iface ) @@ -225,7 +227,7 @@ static HRESULT WINAPI vector_view_voice_information_QueryInterface( IVectorView_ { struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); - TRACE("iface %p, iid %s, out %p stub!\n", iface, debugstr_guid(iid), out); + TRACE("iface %p, iid %s, out %p\n", iface, debugstr_guid(iid), out); if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IInspectable) || @@ -276,23 +278,39 @@ static HRESULT WINAPI vector_view_voice_information_GetTrustLevel( IVectorView_V static HRESULT WINAPI vector_view_voice_information_GetAt( IVectorView_VoiceInformation *iface, UINT32 index, IVoiceInformation **value ) { - FIXME("iface %p, index %#x, value %p stub!\n", iface, index, value); - *value = NULL; - return E_BOUNDS; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + TRACE("iface %p, index %#x, value %p\n", iface, index, value); + if (index >= impl->provider.num_voices) + { + *value = NULL; + return E_BOUNDS; + } + IVoiceInformation_AddRef( *value = impl->provider.voices[index] ); + return S_OK; } static HRESULT WINAPI vector_view_voice_information_get_Size( IVectorView_VoiceInformation *iface, UINT32 *value ) { - FIXME("iface %p, value %p stub!\n", iface, value); - *value = 0; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + TRACE("iface %p, value %p\n", iface, value); + *value = impl->provider.num_voices; return S_OK; } static HRESULT WINAPI vector_view_voice_information_IndexOf( IVectorView_VoiceInformation *iface, IVoiceInformation *element, UINT32 *index, BOOLEAN *found ) { - FIXME("iface %p, element %p, index %p, found %p stub!\n", iface, element, index, found); - *index = 0; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + int i; + + TRACE("iface %p, element %p, index %p, found %p\n", iface, element, index, found); + for (i = 0; i < impl->provider.num_voices; i++) + if (element == impl->provider.voices[i]) + { + *index = i; + *found = TRUE; + return S_OK; + } *found = FALSE; return S_OK; } @@ -300,8 +318,18 @@ static HRESULT WINAPI vector_view_voice_information_IndexOf( IVectorView_VoiceIn static HRESULT WINAPI vector_view_voice_information_GetMany( IVectorView_VoiceInformation *iface, UINT32 start_index, UINT32 items_size, IVoiceInformation **items, UINT *value ) { - FIXME("iface %p, start_index %#x, items %p, value %p stub!\n", iface, start_index, items, value); - *value = 0; + struct voice_information_vector *impl = impl_from_IVectorView_VoiceInformation(iface); + int i; + + TRACE("iface %p, start_index %#x, items %p, value %p\n", iface, start_index, items, value); + if (start_index >= impl->provider.num_voices) + { + *value = 0; + return S_OK; + } + *value = min(impl->provider.num_voices - start_index, items_size); + for (i = 0; i < *value; i++) + IVoiceInformation_AddRef(items[i] = impl->provider.voices[start_index + i]); return S_OK; } @@ -324,7 +352,8 @@ static const struct IVectorView_VoiceInformationVtbl vector_view_voice_informati static struct voice_information_vector all_voices = { {&vector_view_voice_information_vtbl}, - 0 + 0, + {}, }; /* @@ -1107,6 +1136,26 @@ static const struct IActivationFactoryVtbl factory_vtbl = factory_ActivateInstance, }; +static HRESULT dummy_provider_init(struct synth_provider *provider) +{ + HRESULT hr; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + + if (GetUserDefaultLocaleName(locale, ARRAY_SIZE(locale)) > ARRAY_SIZE(locale)) + return E_OUTOFMEMORY; + + provider->voices = calloc(1, sizeof(all_voices.provider.voices[0])); + if (!provider->voices) return E_OUTOFMEMORY; + hr = voice_information_allocate(L"Dummy voice", L"--noid--", locale, VoiceGender_Male, &provider->voices[0]); + if (FAILED(hr)) + { + free(provider->voices); + } + else + provider->num_voices = 1; + return hr; +} + /* * * IInstalledVoicesStatic for SpeechSynthesizer runtimeclass @@ -1115,12 +1164,30 @@ static const struct IActivationFactoryVtbl factory_vtbl = DEFINE_IINSPECTABLE(installed_voices_static, IInstalledVoicesStatic, struct synthesizer_statics, IActivationFactory_iface) +static CRITICAL_SECTION allvoices_cs; +static CRITICAL_SECTION_DEBUG allvoices_critsect_debug = +{ + 0, 0, &allvoices_cs, + { &allvoices_critsect_debug.ProcessLocksList, &allvoices_critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": allvoices_cs") } +}; +static CRITICAL_SECTION allvoices_cs = { &allvoices_critsect_debug, -1, 0, 0, 0, 0 }; + static HRESULT WINAPI installed_voices_static_get_AllVoices( IInstalledVoicesStatic *iface, IVectorView_VoiceInformation **value ) { + HRESULT hr; + TRACE("iface %p, value %p.\n", iface, value); - *value = &all_voices.IVectorView_VoiceInformation_iface; - IVectorView_VoiceInformation_AddRef(*value); - return S_OK; + + EnterCriticalSection(&allvoices_cs); + if (all_voices.provider.num_voices == 0) + hr = dummy_provider_init(&all_voices.provider); + else + hr = S_OK; + if (SUCCEEDED(hr)) + IVectorView_VoiceInformation_AddRef(*value = &all_voices.IVectorView_VoiceInformation_iface); + LeaveCriticalSection(&allvoices_cs); + return hr; } static HRESULT WINAPI installed_voices_static_get_DefaultVoice( IInstalledVoicesStatic *iface, IVoiceInformation **value ) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 9dad84f778b..ed5472489f8 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -966,7 +966,7 @@ static void test_SpeechSynthesizer(void) size = 0xdeadbeef; hr = IVectorView_VoiceInformation_get_Size(voices, &size); ok(hr == S_OK, "IVectorView_VoiceInformation_get_Size voices failed, hr %#lx\n", hr); - todo_wine ok(size != 0 && size != 0xdeadbeef, "IVectorView_VoiceInformation_get_Size returned %u\n", size); + ok(size != 0 && size != 0xdeadbeef, "IVectorView_VoiceInformation_get_Size returned %u\n", size); voice = (IVoiceInformation *)0xdeadbeef; hr = IVectorView_VoiceInformation_GetAt(voices, size, &voice); From ee486d98ffe49ab5debf4ac7a0762187032329ea Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 593/758] windows.media.speech: Add more tests about voice selection. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 34 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index ed5472489f8..408d32b55f3 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -880,7 +880,7 @@ static void test_SpeechSynthesizer(void) IClosable *closable; struct async_inspectable_handler async_inspectable_handler; HMODULE hdll; - HSTRING str, str2; + HSTRING str, str2, default_voice_id; HRESULT hr; UINT32 size, idx; BOOLEAN found; @@ -990,13 +990,18 @@ static void test_SpeechSynthesizer(void) check_comparable_presence(voices, voice); - IVoiceInformation_get_Description(voice, &str2); + hr = IVoiceInformation_get_Description(voice, &str2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); - WindowsDeleteString(str2); + + hr = IVoiceInformation_get_Id(voice, &default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ref = IVoiceInformation_Release(voice); ok(ref == 0, "Got unexpected ref %lu.\n", ref); } + else default_voice_id = NULL; IInstalledVoicesStatic_Release(voices_static); IAgileObject_Release(agile_object); @@ -1013,6 +1018,29 @@ static void test_SpeechSynthesizer(void) hr = IInspectable_QueryInterface(inspectable, &IID_ISpeechSynthesizer, (void **)&synthesizer); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = ISpeechSynthesizer_get_Voice(synthesizer, &voice); + todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + if (default_voice_id) + { + if (hr == S_OK) + { + INT32 cmp; + IVoiceInformation_get_Id(voice, &str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = WindowsDeleteString(str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + IVoiceInformation_Release(voice); + } + + hr = WindowsDeleteString(default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + } + /* Test SynthesizeTextToStreamAsync */ hr = WindowsCreateString(simple_synth_text, wcslen(simple_synth_text), &str); ok(hr == S_OK, "WindowsCreateString failed, hr %#lx\n", hr); From 5807ca52fa10acbe5c45f59f6746b04d0fe8fbd6 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 594/758] windows.media.speech: Implement default voice. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/synthesizer.c | 50 +++++++++++++++++-- dlls/windows.media.speech/tests/speech.c | 61 +++++++++++------------- 2 files changed, 73 insertions(+), 38 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 02e3e76e183..0d352ae8c26 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -33,6 +33,7 @@ struct voice_information HSTRING language; HSTRING description; VoiceGender gender; + BOOL is_static; }; static inline struct voice_information *impl_from_IVoiceInformation( IVoiceInformation *iface ) @@ -81,7 +82,9 @@ static ULONG WINAPI voice_information_Release( IVoiceInformation *iface ) struct voice_information *impl = impl_from_IVoiceInformation(iface); ULONG ref = InterlockedDecrement(&impl->ref); TRACE("iface %p, ref %lu.\n", iface, ref); - /* all voices are (for now) statically allocated in all_voices vector. so don't free them */ + /* only deallocate non static instances */ + if (!ref && !impl->is_static) + voice_information_delete(impl); return ref; } @@ -192,8 +195,8 @@ HRESULT voice_information_allocate(const WCHAR *display_name, const WCHAR *id, c if (SUCCEEDED(hr)) { voice_info->gender = gender; + voice_info->is_static = TRUE; voice_info->IVoiceInformation_iface.lpVtbl = &voice_information_vtbl; - *pvoice = &voice_info->IVoiceInformation_iface; } else @@ -204,6 +207,35 @@ HRESULT voice_information_allocate(const WCHAR *display_name, const WCHAR *id, c return hr; } +HRESULT voice_information_clone(IVoiceInformation *voice, IVoiceInformation **out) +{ + struct voice_information *voice_info; + HRESULT hr; + + voice_info = calloc(1, sizeof(*voice_info)); + if (!voice_info) return E_OUTOFMEMORY; + + hr = IVoiceInformation_get_DisplayName(voice, &voice_info->display_name); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Id(voice, &voice_info->id); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Language(voice, &voice_info->language); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Description(voice, &voice_info->description); + if (SUCCEEDED(hr)) + hr = IVoiceInformation_get_Gender(voice, &voice_info->gender); + if (SUCCEEDED(hr)) + { + voice_info->IVoiceInformation_iface.lpVtbl = &voice_information_vtbl; + voice_info->ref = 1; + *out = &voice_info->IVoiceInformation_iface; + } + else + voice_information_delete(voice_info); + + return hr; +} + /* * * IVectorView_VoiceInformation @@ -1192,8 +1224,18 @@ static HRESULT WINAPI installed_voices_static_get_AllVoices( IInstalledVoicesSta static HRESULT WINAPI installed_voices_static_get_DefaultVoice( IInstalledVoicesStatic *iface, IVoiceInformation **value ) { - FIXME("iface %p, value %p stub!\n", iface, value); - return E_NOTIMPL; + struct IVoiceInformation *static_voice; + HRESULT hr; + + TRACE("iface %p, value %p\n", iface, value); + + EnterCriticalSection(&allvoices_cs); + hr = IVectorView_VoiceInformation_GetAt(&all_voices.IVectorView_VoiceInformation_iface, 0, &static_voice); + if (SUCCEEDED(hr)) + hr = voice_information_clone(static_voice, value); + LeaveCriticalSection(&allvoices_cs); + + return hr; } static const struct IInstalledVoicesStaticVtbl installed_voices_static_vtbl = diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 408d32b55f3..926e92b7875 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -978,30 +978,26 @@ static void test_SpeechSynthesizer(void) ok(size == 0, "IVectorView_VoiceInformation_GetMany returned count %u\n", size); hr = IInstalledVoicesStatic_get_DefaultVoice(voices_static, &voice); - todo_wine ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); + ok(hr == S_OK, "IInstalledVoicesStatic_get_DefaultVoice failed, hr %#lx\n", hr); - if (hr == S_OK) - { - /* check that VoiceInformation in static vector voice are not shared when exposed to user */ - idx = size; - hr = IVectorView_VoiceInformation_IndexOf(voices, voice, &idx, &found); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - ok(!found, "Shouldn't find default element\n"); + /* check that VoiceInformation in static vector voice are not shared when exposed to user */ + idx = size; + hr = IVectorView_VoiceInformation_IndexOf(voices, voice, &idx, &found); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!found, "Shouldn't find default element\n"); - check_comparable_presence(voices, voice); + check_comparable_presence(voices, voice); - hr = IVoiceInformation_get_Description(voice, &str2); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); - WindowsDeleteString(str2); + hr = IVoiceInformation_get_Description(voice, &str2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + trace("SpeechSynthesizer default voice %s.\n", debugstr_hstring(str2)); + WindowsDeleteString(str2); - hr = IVoiceInformation_get_Id(voice, &default_voice_id); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(voice, &default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - ref = IVoiceInformation_Release(voice); - ok(ref == 0, "Got unexpected ref %lu.\n", ref); - } - else default_voice_id = NULL; + ref = IVoiceInformation_Release(voice); + ok(ref == 0, "Got unexpected ref %lu.\n", ref); IInstalledVoicesStatic_Release(voices_static); IAgileObject_Release(agile_object); @@ -1021,26 +1017,23 @@ static void test_SpeechSynthesizer(void) hr = ISpeechSynthesizer_get_Voice(synthesizer, &voice); todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (default_voice_id) + if (hr == S_OK) { - if (hr == S_OK) - { - INT32 cmp; - IVoiceInformation_get_Id(voice, &str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - - hr = WindowsDeleteString(str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - - IVoiceInformation_Release(voice); - } + INT32 cmp; + IVoiceInformation_get_Id(voice, &str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsDeleteString(default_voice_id); + hr = WindowsDeleteString(str); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + IVoiceInformation_Release(voice); } + hr = WindowsDeleteString(default_voice_id); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + /* Test SynthesizeTextToStreamAsync */ hr = WindowsCreateString(simple_synth_text, wcslen(simple_synth_text), &str); ok(hr == S_OK, "WindowsCreateString failed, hr %#lx\n", hr); From fac5b2137bf53b78be70be5b735036f010c95c9a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 4 Jul 2023 12:40:52 +0200 Subject: [PATCH 595/758] windows.media.speech: Implement get/put voice on synthesizer. Signed-off-by: Eric Pouech CW-Bug-Id: #20134 --- dlls/windows.media.speech/synthesizer.c | 57 ++++++++++++++++++++++-- dlls/windows.media.speech/tests/speech.c | 22 +++++---- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/dlls/windows.media.speech/synthesizer.c b/dlls/windows.media.speech/synthesizer.c index 0d352ae8c26..bdf7325f669 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -839,6 +839,7 @@ struct synthesizer LONG ref; struct synthesizer_options *options; + IVoiceInformation *current_voice; }; /* @@ -902,6 +903,8 @@ static ULONG WINAPI synthesizer_Release( ISpeechSynthesizer *iface ) { if (impl->options) ISpeechSynthesizerOptions_Release(&impl->options->ISpeechSynthesizerOptions_iface); + if (impl->current_voice) + IVoiceInformation_Release(impl->current_voice); free(impl); } @@ -954,14 +957,49 @@ static HRESULT WINAPI synthesizer_SynthesizeSsmlToStreamAsync( ISpeechSynthesize static HRESULT WINAPI synthesizer_put_Voice( ISpeechSynthesizer *iface, IVoiceInformation *value ) { - FIXME("iface %p, value %p stub.\n", iface, value); - return E_NOTIMPL; + struct synthesizer *impl = impl_from_ISpeechSynthesizer(iface); + IVoiceInformation *voice; + HSTRING id, id2; + HRESULT hr; + INT32 cmp, idx; + + TRACE("iface %p, value %p semi-stub.\n", iface, value); + + hr = IVoiceInformation_get_Id(value, &id); + if (FAILED(hr)) return hr; + + for (idx = 0; ; idx++) + { + if (SUCCEEDED(hr = IVectorView_VoiceInformation_GetAt(&all_voices.IVectorView_VoiceInformation_iface, idx, &voice))) + { + if (SUCCEEDED(hr = IVoiceInformation_get_Id(voice, &id2))) + { + hr = WindowsCompareStringOrdinal(id, id2, &cmp); + WindowsDeleteString(id2); + } + IVoiceInformation_Release(voice); + } + if (FAILED(hr) || cmp == 0) break; + } + WindowsDeleteString(id); + + if (SUCCEEDED(hr)) + { + if (impl->current_voice) + IVoiceInformation_Release(impl->current_voice); + IVoiceInformation_AddRef(impl->current_voice = value); + } + return hr; } static HRESULT WINAPI synthesizer_get_Voice( ISpeechSynthesizer *iface, IVoiceInformation **value ) { - FIXME("iface %p, value %p stub.\n", iface, value); - return E_NOTIMPL; + struct synthesizer *impl = impl_from_ISpeechSynthesizer(iface); + + TRACE("iface %p, value %p.\n", iface, value); + if (!impl->current_voice) return E_NOTIMPL; + IVoiceInformation_AddRef(*value = impl->current_voice); + return S_OK; } static const struct ISpeechSynthesizerVtbl synthesizer_vtbl = @@ -1136,7 +1174,9 @@ static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLev static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) { + struct IVoiceInformation *static_voice; struct synthesizer *impl; + HRESULT hr; TRACE("iface %p, instance %p.\n", iface, instance); @@ -1149,6 +1189,15 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp impl->ISpeechSynthesizer_iface.lpVtbl = &synthesizer_vtbl; impl->ISpeechSynthesizer2_iface.lpVtbl = &synthesizer2_vtbl; impl->IClosable_iface.lpVtbl = &closable_vtbl; + /* assuming default is the first one... */ + hr = IVectorView_VoiceInformation_GetAt(&all_voices.IVectorView_VoiceInformation_iface, 0, &static_voice); + if (SUCCEEDED(hr)) + hr = voice_information_clone(static_voice, &impl->current_voice); + if (FAILED(hr)) + { + free(impl); + return hr; + } impl->ref = 1; *instance = (IInspectable *)&impl->ISpeechSynthesizer_iface; diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 926e92b7875..1b9198ac10a 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -885,6 +885,7 @@ static void test_SpeechSynthesizer(void) UINT32 size, idx; BOOLEAN found; ULONG ref; + INT32 cmp; hr = RoInitialize(RO_INIT_MULTITHREADED); ok(hr == S_OK, "RoInitialize failed, hr %#lx\n", hr); @@ -1015,21 +1016,18 @@ static void test_SpeechSynthesizer(void) ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); hr = ISpeechSynthesizer_get_Voice(synthesizer, &voice); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - if (hr == S_OK) - { - INT32 cmp; - IVoiceInformation_get_Id(voice, &str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = IVoiceInformation_get_Id(voice, &str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - hr = WindowsDeleteString(str); - ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + hr = WindowsCompareStringOrdinal(str, default_voice_id, &cmp); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - IVoiceInformation_Release(voice); - } + hr = WindowsDeleteString(str); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + IVoiceInformation_Release(voice); hr = WindowsDeleteString(default_voice_id); ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); From b0a86ed12771638b05a2334a8bb24fe73d1de8a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 7 Jul 2023 20:53:07 +0200 Subject: [PATCH 596/758] winegstreamer: Free the media source work queue outside of the CS. Possibly fixing some rare deadlock with MSFS. --- dlls/winegstreamer/media_source_old.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winegstreamer/media_source_old.c b/dlls/winegstreamer/media_source_old.c index c9a76349e82..7dd7a989d4a 100644 --- a/dlls/winegstreamer/media_source_old.c +++ b/dlls/winegstreamer/media_source_old.c @@ -1251,6 +1251,7 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) if (!ref) { IMFMediaSource_Shutdown(iface); + MFUnlockWorkQueue(source->async_commands_queue); IMFPresentationDescriptor_Release(source->pres_desc); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); @@ -1452,8 +1453,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) } free(source->streams); - MFUnlockWorkQueue(source->async_commands_queue); - LeaveCriticalSection(&source->cs); return S_OK; From c503f11319086f6672099519921e91cbda016e00 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Thu, 9 Mar 2023 11:28:41 -0300 Subject: [PATCH 597/758] mf/tests: Test media session error handling. Test error handling for mfsession_Start when a source fails at different stages. Cw-Bug-Id: #21809 (cherry picked from commit 2580799e7068ff405ee55032dcdd8dcb941d604a) --- dlls/mf/tests/mf.c | 183 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 4 deletions(-) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 399f983983f..d4a956b5459 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -41,6 +41,39 @@ #include "initguid.h" #include "evr9.h" +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT(func) \ + do { \ + CHECK_EXPECT2(func); \ + expect_ ## func = FALSE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CHECK_NOT_CALLED(func) \ + do { \ + ok(!called_ ## func, "unexpected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CLEAR_CALLED(func) \ + expect_ ## func = called_ ## func = FALSE + extern GUID DMOVideoFormat_RGB32; HRESULT (WINAPI *pMFCreateSampleCopierMFT)(IMFTransform **copier); @@ -236,10 +269,15 @@ static void init_sink_node(IMFStreamSink *stream_sink, MF_CONNECT_METHOD method, } } +DEFINE_EXPECT(test_source_BeginGetEvent); +DEFINE_EXPECT(test_source_QueueEvent); +DEFINE_EXPECT(test_source_Start); + struct test_source { IMFMediaSource IMFMediaSource_iface; LONG refcount; + HRESULT begin_get_event_res; IMFPresentationDescriptor *pd; }; @@ -294,8 +332,9 @@ static HRESULT WINAPI test_source_GetEvent(IMFMediaSource *iface, DWORD flags, I static HRESULT WINAPI test_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct test_source *source = impl_from_IMFMediaSource(iface); + CHECK_EXPECT(test_source_BeginGetEvent); + return source->begin_get_event_res; } static HRESULT WINAPI test_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) @@ -307,7 +346,7 @@ static HRESULT WINAPI test_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncRes static HRESULT WINAPI test_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) { - ok(0, "Unexpected call.\n"); + CHECK_EXPECT(test_source_QueueEvent); return E_NOTIMPL; } @@ -326,7 +365,7 @@ static HRESULT WINAPI test_source_CreatePresentationDescriptor(IMFMediaSource *i static HRESULT WINAPI test_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *pd, const GUID *time_format, const PROPVARIANT *start_position) { - ok(0, "Unexpected call.\n"); + CHECK_EXPECT(test_source_Start); return E_NOTIMPL; } @@ -372,6 +411,7 @@ static IMFMediaSource *create_test_source(IMFPresentationDescriptor *pd) source = calloc(1, sizeof(*source)); source->IMFMediaSource_iface.lpVtbl = &test_source_vtbl; source->refcount = 1; + source->begin_get_event_res = E_NOTIMPL; IMFPresentationDescriptor_AddRef((source->pd = pd)); return &source->IMFMediaSource_iface; @@ -1914,6 +1954,41 @@ static HRESULT wait_media_event_(int line, IMFMediaSession *session, IMFAsyncCal return status; } +#define wait_media_event_until_blocking(a, b, c, d, e) wait_media_event_until_blocking_(__LINE__, a, b, c, d, e) +static HRESULT wait_media_event_until_blocking_(int line, IMFMediaSession *session, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + MediaEventType type; + HRESULT hr, status; + DWORD ret; + GUID guid; + + do + { + hr = IMFMediaSession_BeginGetEvent(session, &impl->IMFAsyncCallback_iface, (IUnknown *)session); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + if (ret == WAIT_TIMEOUT) return WAIT_TIMEOUT; + hr = IMFMediaEvent_GetType(impl->media_event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } while (type != expect_type); + + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); + + hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); + + hr = IMFMediaEvent_GetValue(impl->media_event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return status; +} + static IMFMediaSource *create_media_source(const WCHAR *name, const WCHAR *mime) { IMFSourceResolver *resolver; @@ -1985,6 +2060,7 @@ static void test_media_session_events(void) struct test_stream_sink stream_sink = test_stream_sink; struct test_media_sink media_sink = test_media_sink; struct test_handler handler = test_handler; + struct test_source *source_impl; IMFAsyncCallback *callback, *callback2; IMFMediaType *input_type, *output_type; IMFTopologyNode *src_node, *sink_node; @@ -2363,6 +2439,105 @@ static void test_media_session_events(void) /* sometimes briefly leaking */ IMFMediaSession_Release(session); + + /* test IMFMediaSession_Start with source returning an error in BeginGetEvent */ + source_impl = impl_from_IMFMediaSource(source); + + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + source_impl->begin_get_event_res = 0x80001234; + + SET_EXPECT(test_source_BeginGetEvent); + SET_EXPECT(test_source_QueueEvent); + SET_EXPECT(test_source_Start); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); + todo_wine ok(hr == 0x80001234, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + CHECK_CALLED(test_source_BeginGetEvent); + todo_wine { CHECK_NOT_CALLED(test_source_Start); } + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE; + + source_impl->begin_get_event_res = E_NOTIMPL; + + CLEAR_CALLED(test_source_BeginGetEvent); + CLEAR_CALLED(test_source_QueueEvent); + CLEAR_CALLED(test_source_Start); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + + + /* test IMFMediaSession_Start when test source BeginGetEvent returns S_OK */ + hr = MFCreateMediaSession(NULL, &session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_SetTopology(session, 0, topology); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event(session, callback, MESessionTopologySet, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_UNKNOWN, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + source_impl = impl_from_IMFMediaSource(source); + source_impl->begin_get_event_res = S_OK; + + SET_EXPECT(test_source_BeginGetEvent); + SET_EXPECT(test_source_QueueEvent); + SET_EXPECT(test_source_Start); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); + todo_wine ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); + PropVariantClear(&propvar); + + CHECK_CALLED(test_source_BeginGetEvent); + CHECK_CALLED(test_source_Start); + + hr = IMFMediaSession_ClearTopologies(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(media_sink.shutdown, "media sink didn't shutdown.\n"); + media_sink.shutdown = FALSE; + + source_impl->begin_get_event_res = E_NOTIMPL; + + CLEAR_CALLED(test_source_BeginGetEvent); + CLEAR_CALLED(test_source_QueueEvent); + CLEAR_CALLED(test_source_Start); + + /* sometimes briefly leaking */ + IMFMediaSession_Release(session); + IMFAsyncCallback_Release(callback); if (handler.current_type) From f5009b4d9fdcb8b1ec61640f92671226e3e8cda5 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Thu, 9 Mar 2023 11:50:30 -0300 Subject: [PATCH 598/758] mf/session: Handle errors when subscribing to source's events. Cw-Bug-Id: #21809 (cherry picked from commit 74b64eab2049c0c6de7dded39bf5ffcf8c921da2) --- dlls/mf/session.c | 18 +++++++++++------- dlls/mf/tests/mf.c | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 004dcc88b98..f940dd2c89f 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -866,13 +866,13 @@ static void session_command_complete_with_event(struct media_session *session, M session_command_complete(session); } -static void session_subscribe_sources(struct media_session *session) +static HRESULT session_subscribe_sources(struct media_session *session) { struct media_source *source; - HRESULT hr; + HRESULT hr = S_OK; if (session->presentation.flags & SESSION_FLAG_SOURCES_SUBSCRIBED) - return; + return hr; LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { @@ -880,10 +880,12 @@ static void session_subscribe_sources(struct media_session *session) source->object))) { WARN("Failed to subscribe to source events, hr %#lx.\n", hr); + return hr; } } session->presentation.flags |= SESSION_FLAG_SOURCES_SUBSCRIBED; + return hr; } static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) @@ -909,7 +911,11 @@ static void session_start(struct media_session *session, const GUID *time_format session->presentation.start_position.vt = VT_EMPTY; PropVariantCopy(&session->presentation.start_position, start_position); - session_subscribe_sources(session); + if (FAILED(hr = session_subscribe_sources(session))) + { + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { @@ -1308,10 +1314,8 @@ static void session_set_rate(struct media_session *session, BOOL thin, float rat if (SUCCEEDED(hr)) hr = IMFRateControl_GetRate(session->clock_rate_control, NULL, &clock_rate); - if (SUCCEEDED(hr) && (rate != clock_rate)) + if (SUCCEEDED(hr) && (rate != clock_rate) && SUCCEEDED(hr = session_subscribe_sources(session))) { - session_subscribe_sources(session); - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { if (SUCCEEDED(hr = MFGetService(source->object, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateControl, diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index d4a956b5459..d2cb0ff792b 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2464,13 +2464,13 @@ static void test_media_session_events(void) hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); - todo_wine ok(hr == 0x80001234, "Unexpected hr %#lx.\n", hr); + ok(hr == 0x80001234, "Unexpected hr %#lx.\n", hr); ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); PropVariantClear(&propvar); CHECK_CALLED(test_source_BeginGetEvent); - todo_wine { CHECK_NOT_CALLED(test_source_Start); } + CHECK_NOT_CALLED(test_source_Start); hr = IMFMediaSession_ClearTopologies(session); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); From cb60c1b9a46f9f79303171a32a815a809608db79 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Fri, 7 Apr 2023 14:07:26 -0300 Subject: [PATCH 599/758] mf/session: Handle error when a source fails to start. Cw-Bug-Id: #21809 (cherry picked from commit dd6b2f9ab5e65142882748f873a1c7c2ed10cb00) --- dlls/mf/session.c | 4 ++++ dlls/mf/tests/mf.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index f940dd2c89f..e4d35f08e6a 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -920,7 +920,11 @@ static void session_start(struct media_session *session, const GUID *time_format LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { if (FAILED(hr = IMFMediaSource_Start(source->source, source->pd, &GUID_NULL, start_position))) + { WARN("Failed to start media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } } session->state = SESSION_STATE_STARTING_SOURCES; diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index d2cb0ff792b..cc28786cf65 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -2513,7 +2513,7 @@ static void test_media_session_events(void) hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = wait_media_event_until_blocking(session, callback, MESessionStarted, 1000, &propvar); - todo_wine ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); ok(propvar.punkVal != (IUnknown *)topology, "got punkVal %p\n", propvar.punkVal); PropVariantClear(&propvar); From fbfa9cd7b80edbc4bb3a341dc647b4dc87da6ed2 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 2 May 2023 18:32:18 -0300 Subject: [PATCH 600/758] mf/session: Reset presentation flags when session_clear_presentation is called. This prevents hangs when a program sets a new topology after stopping the current topology, because if we don't reset the flags to 0 the session will not subscribe to the events of the new topology sources. Cw-Bug-Id: #21809 (cherry picked from commit dedc0684e9de95059a8605e8592d1c734375c5c4) --- dlls/mf/session.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index e4d35f08e6a..75441e99775 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -800,6 +800,7 @@ static void session_clear_presentation(struct media_session *session) IMFTopology_Clear(session->presentation.current_topology); session->presentation.topo_status = MF_TOPOSTATUS_INVALID; + session->presentation.flags = 0; LIST_FOR_EACH_ENTRY_SAFE(source, source2, &session->presentation.sources, struct media_source, entry) { From c044b5a369838248384fee1bdad0671c0f29192b Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Fri, 9 Jun 2023 16:59:27 -0300 Subject: [PATCH 601/758] mf: Clear end of presentation only if topo_status is ready. Cw-Bug-Id: #21809 --- dlls/mf/session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 75441e99775..25e1c474b44 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2570,7 +2570,8 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, case SESSION_CMD_STOP: if (session->presentation.flags & SESSION_FLAG_END_OF_PRESENTATION) session_set_topo_status(session, S_OK, MF_TOPOSTATUS_ENDED); - session_clear_end_of_presentation(session); + if (session->presentation.topo_status == MF_TOPOSTATUS_READY) + session_clear_end_of_presentation(session); session_stop(session); break; case SESSION_CMD_CLOSE: From 98a59462931caaf6e13983d9d2d950bb19899717 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 10 Jul 2023 14:09:22 -0600 Subject: [PATCH 602/758] include: Add initial ioring definitions. CW-Bug-Id: #22414 --- include/Makefile.in | 2 ++ include/ioringapi.h | 42 ++++++++++++++++++++++++++++++++++++++++++ include/ntioring_x.h | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 include/ioringapi.h create mode 100644 include/ntioring_x.h diff --git a/include/Makefile.in b/include/Makefile.in index 71b2ad01ab8..e9498404086 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -371,6 +371,7 @@ SOURCES = \ inspectable.idl \ interactioncontext.h \ intshcut.h \ + ioringapi.h \ ip2string.h \ ipexport.h \ iphlpapi.h \ @@ -570,6 +571,7 @@ SOURCES = \ ntdef.h \ ntdsapi.h \ ntgdi.h \ + ntioring_x.h \ ntlsa.h \ ntquery.h \ ntsecapi.h \ diff --git a/include/ioringapi.h b/include/ioringapi.h new file mode 100644 index 00000000000..36954869ea8 --- /dev/null +++ b/include/ioringapi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Paul Gofman 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 + */ + +#ifndef __IORINGAPI_H_ +#define __IORINGAPI_H_ + +#include "ntioring_x.h" + +struct IORING_CAPABILITIES +{ + IORING_VERSION MaxVersion; + UINT32 MaxSubmissionQueueSize; + UINT32 MaxCompletionQueueSize; + IORING_FEATURE_FLAGS FeatureFlags; +}; +typedef struct IORING_CAPABILITIES IORING_CAPABILITIES; + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT WINAPI QueryIoRingCapabilities(IORING_CAPABILITIES *caps); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/ntioring_x.h b/include/ntioring_x.h new file mode 100644 index 00000000000..40be1f28c54 --- /dev/null +++ b/include/ntioring_x.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Paul Gofman 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 + */ + +#ifndef __NTIORING_X_H_ +#define __NTIORING_X_H_ +enum IORING_VERSION +{ + IORING_VERSION_INVALID = 0, + IORING_VERSION_1 = 1, + IORING_VERSION_2 = 2, + IORING_VERSION_3 = 300, +}; +typedef enum IORING_VERSION IORING_VERSION; + +enum IORING_FEATURE_FLAGS +{ + IORING_FEATURE_FLAGS_NONE = 0, + IORING_FEATURE_UM_EMULATION = 0x00000001, + IORING_FEATURE_SET_COMPLETION_EVENT = 0x00000002, +}; +typedef enum IORING_FEATURE_FLAGS IORING_FEATURE_FLAGS; +#endif From fd1fe3b96b612cbdb420a7e8b6b273c7b52356d9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 10 Jul 2023 14:20:40 -0600 Subject: [PATCH 603/758] kernelbase: Add stub for QueryIoRingCapabilities(). CW-Bug-Id: #22414 --- dlls/kernelbase/file.c | 12 +++++++ dlls/kernelbase/kernelbase.spec | 1 + dlls/kernelbase/tests/Makefile.in | 1 + dlls/kernelbase/tests/file.c | 57 +++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 dlls/kernelbase/tests/file.c diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index ac04388acde..0088bc8747b 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -38,6 +38,7 @@ #include "shlwapi.h" #include "ddk/ntddk.h" #include "ddk/ntddser.h" +#include "ioringapi.h" #include "kernelbase.h" #include "wine/exception.h" @@ -4475,3 +4476,14 @@ BOOL WINAPI DECLSPEC_HOTPATCH WaitCommEvent( HANDLE handle, DWORD *events, OVERL return DeviceIoControl( handle, IOCTL_SERIAL_WAIT_ON_MASK, NULL, 0, events, sizeof(*events), NULL, overlapped ); } + + +/*********************************************************************** + * QueryIoRingCapabilities (kernelbase.@) + */ +HRESULT WINAPI QueryIoRingCapabilities(IORING_CAPABILITIES *caps) +{ + FIXME( "caps %p stub.\n", caps ); + + return E_NOTIMPL; +} diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index fc687efb913..c14f2a87472 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1233,6 +1233,7 @@ @ stdcall QueryDosDeviceW(wstr ptr long) @ stdcall QueryFullProcessImageNameA(ptr long ptr ptr) @ stdcall QueryFullProcessImageNameW(ptr long ptr ptr) +@ stdcall QueryIoRingCapabilities(ptr) # @ stub QueryIdleProcessorCycleTime # @ stub QueryIdleProcessorCycleTimeEx # @ stub QueryInterruptTime diff --git a/dlls/kernelbase/tests/Makefile.in b/dlls/kernelbase/tests/Makefile.in index 675054c753d..b51eaefcd13 100644 --- a/dlls/kernelbase/tests/Makefile.in +++ b/dlls/kernelbase/tests/Makefile.in @@ -1,6 +1,7 @@ TESTDLL = kernelbase.dll C_SRCS = \ + file.c \ path.c \ process.c \ sync.c diff --git a/dlls/kernelbase/tests/file.c b/dlls/kernelbase/tests/file.c new file mode 100644 index 00000000000..18dff0a32ab --- /dev/null +++ b/dlls/kernelbase/tests/file.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 Paul Gofman 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 +#include + +#include +#define WIN32_NO_STATUS +#include +#include +#include +#include + +#include "wine/test.h" + +static HRESULT (WINAPI *pQueryIoRingCapabilities)(IORING_CAPABILITIES *); + +static void test_ioring_caps(void) +{ + IORING_CAPABILITIES caps; + HRESULT hr; + + if (!pQueryIoRingCapabilities) + { + win_skip("QueryIoRingCapabilities is not available, skipping tests.\n"); + return; + } + + memset(&caps, 0xcc, sizeof(caps)); + hr = pQueryIoRingCapabilities(&caps); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); +} + +START_TEST(file) +{ + HMODULE hmod; + + hmod = LoadLibraryA("kernelbase.dll"); + pQueryIoRingCapabilities = (void *)GetProcAddress(hmod, "QueryIoRingCapabilities"); + + test_ioring_caps(); +} From ecb87b8034b31a04f1c957fa7684dc811d14e279 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Wed, 3 Aug 2022 16:25:11 +1000 Subject: [PATCH 604/758] dmime: Store WAVE data when Loading. Wine-Staging: dmime-load-wave CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 56 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index bf44c5e73b3..6bf9f3abf0c 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -33,6 +33,10 @@ typedef struct IDirectMusicSegment8Impl { DMUS_IO_SEGMENT_HEADER header; IDirectMusicGraph *pGraph; struct list Tracks; + + PCMWAVEFORMAT wave_format; + void *wave_data; + int data_size; } IDirectMusicSegment8Impl; IDirectMusicSegment8Impl *create_segment(void); @@ -86,6 +90,9 @@ static ULONG WINAPI IDirectMusicSegment8Impl_Release(IDirectMusicSegment8 *iface TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { + if (This->wave_data) + free(This->wave_data); + HeapFree(GetProcessHeap(), 0, This); DMIME_UnlockModule(); } @@ -818,6 +825,49 @@ static inline IDirectMusicSegment8Impl *impl_from_IPersistStream(IPersistStream return CONTAINING_RECORD(iface, IDirectMusicSegment8Impl, dmobj.IPersistStream_iface); } +static HRESULT parse_wave_form(IDirectMusicSegment8Impl *This, IStream *stream, const struct chunk_entry *riff) +{ + HRESULT hr; + struct chunk_entry chunk = {.parent = riff}; + + TRACE("Parsing segment wave in %p: %s\n", stream, debugstr_chunk(riff)); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + case mmioFOURCC('f','m','t',' '): { + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &This->wave_format, chunk.size))) + return hr; + TRACE("Wave Format tag %d\n", This->wave_format.wf.wFormatTag); + break; + } + case mmioFOURCC('d','a','t','a'): { + TRACE("Wave Data size %lu\n", chunk.size); + This->wave_data = malloc(chunk.size); + This->data_size = chunk.size; + if (!This->wave_data) + return E_OUTOFMEMORY; + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, This->wave_data, chunk.size))) + return hr; + break; + } + case FOURCC_LIST: { + FIXME("Skipping LIST tag\n"); + break; + } + case mmioFOURCC('I','S','F','T'): { + FIXME("Skipping ISFT tag\n"); + break; + } + case mmioFOURCC('f','a','c','t'): { + FIXME("Skipping fact tag\n"); + break; + } + } + } + + return S_OK; +} + static HRESULT WINAPI seg_IPersistStream_Load(IPersistStream *iface, IStream *stream) { IDirectMusicSegment8Impl *This = impl_from_IPersistStream(iface); @@ -847,10 +897,8 @@ static HRESULT WINAPI seg_IPersistStream_Load(IPersistStream *iface, IStream *st if (riff.type == DMUS_FOURCC_SEGMENT_FORM) hr = parse_segment_form(This, stream, &riff); - else { - FIXME("WAVE form loading not implemented\n"); - hr = S_OK; - } + else + hr = parse_wave_form(This, stream, &riff); return hr; } From 5c7376d54b0cbf350242108c27f9b5f5b2c6de65 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Fri, 2 Dec 2022 14:41:30 +1100 Subject: [PATCH 605/758] dmime: Implement IDirectMusicSegment8 Download Wine-Staging: dmime-load-wave CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 2 + dlls/dmime/performance.c | 7 +++ dlls/dmime/segment.c | 87 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 4159abffa99..030aab50094 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -71,6 +71,8 @@ extern void set_audiopath_perf_pointer(IDirectMusicAudioPath*,IDirectMusicPerfor extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*) DECLSPEC_HIDDEN; extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*) DECLSPEC_HIDDEN; +extern IDirectSound *get_dsound_interface(IDirectMusicPerformance8*) DECLSPEC_HIDDEN; + /***************************************************************************** * Auxiliary definitions */ diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index d69a27540d6..5578c3e523b 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -252,6 +252,13 @@ static inline IDirectMusicPerformance8Impl *impl_from_IDirectMusicPerformance8(I return CONTAINING_RECORD(iface, IDirectMusicPerformance8Impl, IDirectMusicPerformance8_iface); } +IDirectSound *get_dsound_interface(IDirectMusicPerformance8* iface) +{ + IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + return This->dsound; +} + + /* IDirectMusicPerformance8 IUnknown part: */ static HRESULT WINAPI IDirectMusicPerformance8Impl_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ppv) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 6bf9f3abf0c..0ea0c15c5e0 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -37,6 +37,7 @@ typedef struct IDirectMusicSegment8Impl { PCMWAVEFORMAT wave_format; void *wave_data; int data_size; + IDirectSoundBuffer *buffer; } IDirectMusicSegment8Impl; IDirectMusicSegment8Impl *create_segment(void); @@ -90,6 +91,8 @@ static ULONG WINAPI IDirectMusicSegment8Impl_Release(IDirectMusicSegment8 *iface TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { + if (This->buffer) + IDirectSoundBuffer_Release(This->buffer); if (This->wave_data) free(This->wave_data); @@ -559,9 +562,87 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_Compose(IDirectMusicSegment8 *ifa static HRESULT WINAPI IDirectMusicSegment8Impl_Download(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): stub\n", This, pAudioPath); - return S_OK; + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicPerformance8 *perf; + IDirectMusicAudioPath *audio; + IDirectSound *dsound; + HRESULT hr; + DSBUFFERDESC dsbd = {.dwSize = sizeof(dsbd)}; + void *data; + DWORD size; + DWORD buffer = 0; + + TRACE("(%p, %p)\n", This, pAudioPath); + + if (!pAudioPath) + return E_INVALIDARG; + + if (This->buffer) + { + TRACE("Using Cached buffer\n"); + return S_OK; + } + + /* pAudioPath can either be IDirectMusicAudioPath or IDirectMusicPerformance */ + hr = IUnknown_QueryInterface(pAudioPath, &IID_IDirectMusicPerformance8, (void**)&perf); + if (FAILED(hr)) + { + TRACE("Checking for IDirectMusicAudioPath interface\n"); + hr = IUnknown_QueryInterface(pAudioPath, &IID_IDirectMusicAudioPath, (void**)&audio); + if (FAILED(hr)) + { + WARN("Cannot query for IDirectMusicAudioPath\n"); + return E_INVALIDARG; + } + + IDirectMusicAudioPath_GetObjectInPath(audio, DMUS_PCHANNEL_ALL, DMUS_PATH_PERFORMANCE, buffer, &GUID_NULL, + 0, &IID_IDirectMusicPerformance, (void**)&perf); + IDirectMusicAudioPath_Release(audio); + } + + if (!perf) + { + ERR("Failed to get IDirectMusicPerformance interface\n"); + return E_INVALIDARG; + } + + dsound = get_dsound_interface(perf); + if (!dsound) + { + ERR("Failed get_dsound_interface\n"); + return E_INVALIDARG; + } + + if (This->data_size == 0) + { + FIXME("No wave data skipping\n"); + return S_OK; + } + + dsbd.dwBufferBytes = This->data_size; + dsbd.lpwfxFormat = (WAVEFORMATEX*)&This->wave_format; + + hr = IDirectSound_CreateSoundBuffer(dsound, &dsbd, &This->buffer, NULL); + if (FAILED(hr)) + { + ERR("IDirectSound_CreateSoundBuffer failed 0x%08lx\n", hr); + return E_INVALIDARG; + } + + TRACE("CreateSoundBuffer successful\n"); + + hr = IDirectSoundBuffer_Lock(This->buffer, 0, This->data_size, &data, &size, NULL, 0, 0); + TRACE("IDirectSoundBuffer_Lock hr 0x%08lx\n", hr); + + memcpy(data, This->wave_data, This->data_size); + + hr = IDirectSoundBuffer_Unlock(This->buffer, data, This->data_size, NULL, 0); + TRACE("IDirectSoundBuffer_Unlock hr 0x%08lx\n", hr); + + /*hr = IDirectSoundBuffer_Play(This->buffer, 0, 0, 0); + TRACE("IDirectSoundBuffer_Play hr 0x%08lx\n", hr);*/ + + return S_OK; } static HRESULT WINAPI IDirectMusicSegment8Impl_Unload(IDirectMusicSegment8 *iface, From 54ce9274adb005b520dcc02e8c09329546f4de88 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Mon, 12 Dec 2022 15:20:10 +1100 Subject: [PATCH 606/758] dmime: Play a sound in IDirectMusicPerformance8 PlaySegmentEx Wine-Staging: dmime-load-wave CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 1 + dlls/dmime/performance.c | 25 +++++++++++++++++++------ dlls/dmime/segment.c | 6 ++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 030aab50094..d09aba02a5c 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -72,6 +72,7 @@ extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffe extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*) DECLSPEC_HIDDEN; extern IDirectSound *get_dsound_interface(IDirectMusicPerformance8*) DECLSPEC_HIDDEN; +extern IDirectSoundBuffer *get_segment_buffer(IDirectMusicSegment8 *iface) DECLSPEC_HIDDEN; /***************************************************************************** * Auxiliary definitions diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 5578c3e523b..03e59e95af3 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1043,13 +1043,26 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_PlaySegmentEx(IDirectMusicPer __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState, IUnknown *pFrom, IUnknown *pAudioPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + IDirectMusicSegment8 *segment; + IDirectSoundBuffer *buffer; + HRESULT hr; - FIXME("(%p, %p, %p, %p, %ld, 0x%s, %p, %p, %p): stub\n", This, pSource, pwzSegmentName, - pTransition, dwFlags, wine_dbgstr_longlong(i64StartTime), ppSegmentState, pFrom, pAudioPath); - if (ppSegmentState) - return create_dmsegmentstate(&IID_IDirectMusicSegmentState,(void**)ppSegmentState); - return S_OK; + FIXME("(%p, %p, %p, %p, %ld, 0x%s, %p, %p, %p): semi-stub\n", This, pSource, pwzSegmentName, + pTransition, dwFlags, wine_dbgstr_longlong(i64StartTime), ppSegmentState, pFrom, pAudioPath); + + hr = IUnknown_QueryInterface(pSource, &IID_IDirectMusicSegment8, (void**)&segment); + if (FAILED(hr)) + return hr; + + buffer = get_segment_buffer(segment); + + if (segment) + hr = IDirectSoundBuffer_Play(buffer, 0, 0, 0); + + if (ppSegmentState) + return create_dmsegmentstate(&IID_IDirectMusicSegmentState,(void**)ppSegmentState); + return S_OK; } static HRESULT WINAPI IDirectMusicPerformance8Impl_StopEx(IDirectMusicPerformance8 *iface, diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 0ea0c15c5e0..b21f93bbfc6 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -47,6 +47,12 @@ static inline IDirectMusicSegment8Impl *impl_from_IDirectMusicSegment8(IDirectMu return CONTAINING_RECORD(iface, IDirectMusicSegment8Impl, IDirectMusicSegment8_iface); } +IDirectSoundBuffer *get_segment_buffer(IDirectMusicSegment8 *iface) +{ + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + return This->buffer; +} + static HRESULT WINAPI IDirectMusicSegment8Impl_QueryInterface(IDirectMusicSegment8 *iface, REFIID riid, void **ret_iface) { From 6b0005022b6e68d36c84a6f51632596d44457857 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 11 Jul 2023 14:29:15 -0600 Subject: [PATCH 607/758] ntdll: HACK: Enable WINE_SIMULATE_WRITECOPY for Idol Hands 2. CW-Bug-Id: #22425 --- dlls/ntdll/unix/loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index dc9283bc7eb..c4056e8934a 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2361,6 +2361,7 @@ static void hacks_init(void) else if (sgi) simulate_writecopy = !strcmp(sgi, "1608730") /* Dawn of Corruption */ || !strcmp(sgi, "1680700") /* Purgo box */ || !strcmp(sgi, "2095300") /* Breakout 13 */ + || !strcmp(sgi, "2053940") /* Idol Hands 2 */ || !strcmp(sgi, "2176450"); /* Mr. Hopp's Playhouse 3 */ if (main_argc > 1 && strstr(main_argv[1], "MicrosoftEdgeUpdate.exe")) From c665e13d276c92c2cdc90d66028e2f2866e958fb Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Thu, 17 Apr 2014 16:07:46 -0600 Subject: [PATCH 608/758] server: Unify the storage of security attributes for files and directories. (try 7) CW-Bug-Id: #22436 --- server/change.c | 45 ++++++--------------------------------------- server/file.c | 34 ++++++++++++++++++++++------------ server/file.h | 2 ++ 3 files changed, 30 insertions(+), 51 deletions(-) diff --git a/server/change.c b/server/change.c index df79e5c8524..92f35028c5b 100644 --- a/server/change.c +++ b/server/change.c @@ -366,48 +366,15 @@ static int dir_set_sd( struct object *obj, const struct security_descriptor *sd, unsigned int set_info ) { struct dir *dir = (struct dir *)obj; - const struct sid *owner; - struct stat st; - mode_t mode; - int unix_fd; + struct fd *fd; + int ret; assert( obj->ops == &dir_ops ); - unix_fd = get_dir_unix_fd( dir ); - - if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1; - - if (set_info & OWNER_SECURITY_INFORMATION) - { - owner = sd_get_owner( sd ); - if (!owner) - { - set_error( STATUS_INVALID_SECURITY_DESCR ); - return 0; - } - if (!obj->sd || !equal_sid( owner, sd_get_owner( obj->sd ) )) - { - /* FIXME: get Unix uid and call fchown */ - } - } - else if (obj->sd) - owner = sd_get_owner( obj->sd ); - else - owner = token_get_owner( current->process->token ); - - if (set_info & DACL_SECURITY_INFORMATION) - { - /* keep the bits that we don't map to access rights in the ACL */ - mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); - mode |= sd_to_mode( sd, owner ); - - if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1) - { - file_set_error(); - return 0; - } - } - return 1; + fd = dir_get_fd( obj ); + ret = set_file_sd( obj, fd, &dir->mode, &dir->uid, sd, set_info ); + release_object( fd ); + return ret; } static struct change_record *get_first_change_record( struct dir *dir ) diff --git a/server/file.c b/server/file.c index bea0d09f9de..5bbdd210a37 100644 --- a/server/file.c +++ b/server/file.c @@ -500,18 +500,13 @@ mode_t sd_to_mode( const struct security_descriptor *sd, const struct sid *owner return new_mode; } -static int file_set_sd( struct object *obj, const struct security_descriptor *sd, - unsigned int set_info ) +int set_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid, + const struct security_descriptor *sd, unsigned int set_info ) { - struct file *file = (struct file *)obj; + int unix_fd = get_unix_fd( fd ); const struct sid *owner; struct stat st; - mode_t mode; - int unix_fd; - - assert( obj->ops == &file_ops ); - - unix_fd = get_file_unix_fd( file ); + mode_t new_mode; if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1; @@ -538,10 +533,10 @@ static int file_set_sd( struct object *obj, const struct security_descriptor *sd if (set_info & DACL_SECURITY_INFORMATION) { /* keep the bits that we don't map to access rights in the ACL */ - mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); - mode |= sd_to_mode( sd, owner ); + new_mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); + new_mode |= sd_to_mode( sd, owner ); - if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1) + if (((st.st_mode ^ new_mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, new_mode ) == -1) { file_set_error(); return 0; @@ -586,6 +581,21 @@ static struct list *file_get_kernel_obj_list( struct object *obj ) return &file->kernel_object; } +static int file_set_sd( struct object *obj, const struct security_descriptor *sd, + unsigned int set_info ) +{ + struct file *file = (struct file *)obj; + struct fd *fd; + int ret; + + assert( obj->ops == &file_ops ); + + fd = file_get_fd( obj ); + ret = set_file_sd( obj, fd, &file->mode, &file->uid, sd, set_info ); + release_object( fd ); + return ret; +} + static void file_destroy( struct object *obj ) { struct file *file = (struct file *)obj; diff --git a/server/file.h b/server/file.h index 1965626dc0e..189cc7af931 100644 --- a/server/file.h +++ b/server/file.h @@ -176,6 +176,8 @@ extern void file_set_error(void); extern struct security_descriptor *mode_to_sd( mode_t mode, const struct sid *user, const struct sid *group ); extern mode_t sd_to_mode( const struct security_descriptor *sd, const struct sid *owner ); extern int is_file_executable( const char *name ); +extern int set_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid, + const struct security_descriptor *sd, unsigned int set_info ); /* file mapping functions */ From 08f6a33bdbb2ee303bb6bae4eb29d5b9430343ee Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Thu, 17 Apr 2014 16:07:50 -0600 Subject: [PATCH 609/758] server: Unify the retrieval of security attributes for files and directories. (try 7) CW-Bug-Id: #22436 --- server/change.c | 32 +++++--------------------------- server/file.c | 32 +++++++++++++++++++++----------- server/file.h | 2 ++ 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/server/change.c b/server/change.c index 92f35028c5b..f6d3d0252a9 100644 --- a/server/change.c +++ b/server/change.c @@ -326,39 +326,17 @@ static struct fd *dir_get_fd( struct object *obj ) return (struct fd *)grab_object( dir->fd ); } -static int get_dir_unix_fd( struct dir *dir ) -{ - return get_unix_fd( dir->fd ); -} - static struct security_descriptor *dir_get_sd( struct object *obj ) { struct dir *dir = (struct dir *)obj; - int unix_fd; - struct stat st; struct security_descriptor *sd; - assert( obj->ops == &dir_ops ); - - unix_fd = get_dir_unix_fd( dir ); - - if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) - return obj->sd; - - /* mode and uid the same? if so, no need to re-generate security descriptor */ - if (obj->sd && - (st.st_mode & (S_IRWXU|S_IRWXO)) == (dir->mode & (S_IRWXU|S_IRWXO)) && - (st.st_uid == dir->uid)) - return obj->sd; + struct fd *fd; - sd = mode_to_sd( st.st_mode, - security_unix_uid_to_sid( st.st_uid ), - token_get_primary_group( current->process->token )); - if (!sd) return obj->sd; + assert( obj->ops == &dir_ops ); - dir->mode = st.st_mode; - dir->uid = st.st_uid; - free( obj->sd ); - obj->sd = sd; + fd = dir_get_fd( obj ); + sd = get_file_sd( obj, fd, &dir->mode, &dir->uid ); + release_object( fd ); return sd; } diff --git a/server/file.c b/server/file.c index 5bbdd210a37..0b51b9d3a2f 100644 --- a/server/file.c +++ b/server/file.c @@ -390,23 +390,19 @@ struct security_descriptor *mode_to_sd( mode_t mode, const struct sid *user, con return sd; } -static struct security_descriptor *file_get_sd( struct object *obj ) +struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode_t *mode, + uid_t *uid ) { - struct file *file = (struct file *)obj; + int unix_fd = get_unix_fd( fd ); struct stat st; - int unix_fd; struct security_descriptor *sd; - assert( obj->ops == &file_ops ); - - unix_fd = get_file_unix_fd( file ); - if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return obj->sd; /* mode and uid the same? if so, no need to re-generate security descriptor */ - if (obj->sd && (st.st_mode & (S_IRWXU|S_IRWXO)) == (file->mode & (S_IRWXU|S_IRWXO)) && - (st.st_uid == file->uid)) + if (obj->sd && (st.st_mode & (S_IRWXU|S_IRWXO)) == (*mode & (S_IRWXU|S_IRWXO)) && + (st.st_uid == *uid)) return obj->sd; sd = mode_to_sd( st.st_mode, @@ -414,13 +410,27 @@ static struct security_descriptor *file_get_sd( struct object *obj ) token_get_primary_group( current->process->token )); if (!sd) return obj->sd; - file->mode = st.st_mode; - file->uid = st.st_uid; + *mode = st.st_mode; + *uid = st.st_uid; free( obj->sd ); obj->sd = sd; return sd; } +static struct security_descriptor *file_get_sd( struct object *obj ) +{ + struct file *file = (struct file *)obj; + struct security_descriptor *sd; + struct fd *fd; + + assert( obj->ops == &file_ops ); + + fd = file_get_fd( obj ); + sd = get_file_sd( obj, fd, &file->mode, &file->uid ); + release_object( fd ); + return sd; +} + static mode_t file_access_to_mode( unsigned int access ) { mode_t mode = 0; diff --git a/server/file.h b/server/file.h index 189cc7af931..796027fcb76 100644 --- a/server/file.h +++ b/server/file.h @@ -178,6 +178,8 @@ extern mode_t sd_to_mode( const struct security_descriptor *sd, const struct sid extern int is_file_executable( const char *name ); extern int set_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid, const struct security_descriptor *sd, unsigned int set_info ); +extern struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode_t *mode, + uid_t *uid ); /* file mapping functions */ From a23fceb2d3660adad60649308a14732b20c4d67e Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Mon, 30 Mar 2015 12:32:34 +0200 Subject: [PATCH 610/758] server: Add a helper function set_sd_from_token_internal to merge two security descriptors. CW-Bug-Id: #22436 --- server/object.c | 59 +++++++++++++++++++++++++++++++------------------ server/object.h | 3 +++ 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/server/object.c b/server/object.c index 89e541ffb6b..29f1ea96129 100644 --- a/server/object.c +++ b/server/object.c @@ -548,8 +548,9 @@ struct security_descriptor *default_get_sd( struct object *obj ) return obj->sd; } -int set_sd_defaults_from_token( struct object *obj, const struct security_descriptor *sd, - unsigned int set_info, struct token *token ) +struct security_descriptor *set_sd_from_token_internal( const struct security_descriptor *sd, + const struct security_descriptor *old_sd, + unsigned int set_info, struct token *token ) { struct security_descriptor new_sd, *new_sd_ptr; int present; @@ -558,8 +559,6 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri struct acl *replaced_sacl = NULL; char *ptr; - if (!set_info) return 1; - new_sd.control = sd->control & ~SE_SELF_RELATIVE; if (set_info & OWNER_SECURITY_INFORMATION && sd->owner_len) @@ -567,10 +566,10 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri owner = sd_get_owner( sd ); new_sd.owner_len = sd->owner_len; } - else if (obj->sd && obj->sd->owner_len) + else if (old_sd && old_sd->owner_len) { - owner = sd_get_owner( obj->sd ); - new_sd.owner_len = obj->sd->owner_len; + owner = sd_get_owner( old_sd ); + new_sd.owner_len = old_sd->owner_len; } else if (token) { @@ -584,10 +583,10 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri group = sd_get_group( sd ); new_sd.group_len = sd->group_len; } - else if (obj->sd && obj->sd->group_len) + else if (old_sd && old_sd->group_len) { - group = sd_get_group( obj->sd ); - new_sd.group_len = obj->sd->group_len; + group = sd_get_group( old_sd ); + new_sd.group_len = old_sd->group_len; } else if (token) { @@ -605,20 +604,20 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri else if (set_info & LABEL_SECURITY_INFORMATION && present) { const struct acl *old_sacl = NULL; - if (obj->sd && obj->sd->control & SE_SACL_PRESENT) old_sacl = sd_get_sacl( obj->sd, &present ); - if (!(replaced_sacl = replace_security_labels( old_sacl, sacl ))) return 0; + if (old_sd && old_sd->control & SE_SACL_PRESENT) old_sacl = sd_get_sacl( old_sd, &present ); + if (!(replaced_sacl = replace_security_labels( old_sacl, sacl ))) return NULL; new_sd.control |= SE_SACL_PRESENT; new_sd.sacl_len = replaced_sacl->size; sacl = replaced_sacl; } else { - if (obj->sd) sacl = sd_get_sacl( obj->sd, &present ); + if (old_sd) sacl = sd_get_sacl( old_sd, &present ); - if (obj->sd && present) + if (old_sd && present) { new_sd.control |= SE_SACL_PRESENT; - new_sd.sacl_len = obj->sd->sacl_len; + new_sd.sacl_len = old_sd->sacl_len; } else new_sd.sacl_len = 0; @@ -632,12 +631,12 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri } else { - if (obj->sd) dacl = sd_get_dacl( obj->sd, &present ); + if (old_sd) dacl = sd_get_dacl( old_sd, &present ); - if (obj->sd && present) + if (old_sd && present) { new_sd.control |= SE_DACL_PRESENT; - new_sd.dacl_len = obj->sd->dacl_len; + new_sd.dacl_len = old_sd->dacl_len; } else if (token) { @@ -653,7 +652,7 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri if (!ptr) { free( replaced_sacl ); - return 0; + return NULL; } new_sd_ptr = (struct security_descriptor*)ptr; @@ -668,9 +667,25 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri memcpy( ptr, dacl, new_sd.dacl_len ); free( replaced_sacl ); - free( obj->sd ); - obj->sd = new_sd_ptr; - return 1; + return new_sd_ptr; +} + +int set_sd_defaults_from_token( struct object *obj, const struct security_descriptor *sd, + unsigned int set_info, struct token *token ) +{ + struct security_descriptor *new_sd; + + if (!set_info) return 1; + + new_sd = set_sd_from_token_internal( sd, obj->sd, set_info, token ); + if (new_sd) + { + free( obj->sd ); + obj->sd = new_sd; + return 1; + } + + return 0; } /** Set the security descriptor using the current primary token for defaults. */ diff --git a/server/object.h b/server/object.h index 8bf236abb58..20c7ab63938 100644 --- a/server/object.h +++ b/server/object.h @@ -175,6 +175,9 @@ extern struct fd *no_get_fd( struct object *obj ); extern unsigned int default_map_access( struct object *obj, unsigned int access ); extern struct security_descriptor *default_get_sd( struct object *obj ); extern int default_set_sd( struct object *obj, const struct security_descriptor *sd, unsigned int set_info ); +extern struct security_descriptor *set_sd_from_token_internal( const struct security_descriptor *sd, + const struct security_descriptor *old_sd, + unsigned int set_info, struct token *token ); extern int set_sd_defaults_from_token( struct object *obj, const struct security_descriptor *sd, unsigned int set_info, struct token *token ); extern WCHAR *no_get_full_name( struct object *obj, data_size_t *ret_len ); From 17045f53d6d9e9e2b6758a7a3195cb10fd0d37ee Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Mon, 30 Mar 2015 12:50:21 +0200 Subject: [PATCH 611/758] server: Temporarily store the full security descriptor for file objects. CW-Bug-Id: #22436 --- dlls/advapi32/tests/security.c | 16 ++---- server/change.c | 8 ++- server/file.c | 100 +++++++++++++++++++++------------ server/file.h | 3 +- 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index c0fea3c5507..05659e05264 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -3774,7 +3774,6 @@ static void test_CreateDirectoryA(void) ok(error == ERROR_SUCCESS, "GetNamedSecurityInfo failed with error %ld\n", error); bret = GetAclInformation(pDacl, &acl_size, sizeof(acl_size), AclSizeInformation); ok(bret, "GetAclInformation failed\n"); - todo_wine ok(acl_size.AceCount == 0, "GetAclInformation returned unexpected entry count (%ld != 0).\n", acl_size.AceCount); LocalFree(pSD); @@ -3785,7 +3784,6 @@ static void test_CreateDirectoryA(void) ok(error == ERROR_SUCCESS, "GetNamedSecurityInfo failed with error %ld\n", error); bret = GetAclInformation(pDacl, &acl_size, sizeof(acl_size), AclSizeInformation); ok(bret, "GetAclInformation failed\n"); - todo_wine ok(acl_size.AceCount == 0, "GetAclInformation returned unexpected entry count (%ld != 0).\n", acl_size.AceCount); LocalFree(pSD); @@ -3908,7 +3906,6 @@ static void test_CreateDirectoryA(void) ok(error == ERROR_SUCCESS, "GetNamedSecurityInfo failed with error %d\n", error); bret = GetAclInformation(pDacl, &acl_size, sizeof(acl_size), AclSizeInformation); ok(bret, "GetAclInformation failed\n"); - todo_wine ok(acl_size.AceCount == 0, "GetAclInformation returned unexpected entry count (%d != 0).\n", acl_size.AceCount); LocalFree(pSD); @@ -5024,23 +5021,22 @@ static void test_GetSecurityInfo(void) bret = GetAce(pDacl, 0, (VOID **)&ace); ok(bret, "Failed to get Current User ACE.\n"); bret = EqualSid(&ace->SidStart, user_sid); - todo_wine ok(bret, "Current User ACE (%s) != Current User SID (%s).\n", - debugstr_sid(&ace->SidStart), debugstr_sid(user_sid)); + ok(bret, "Current User ACE (%s) != Current User SID (%s).\n", debugstr_sid(&ace->SidStart), debugstr_sid(user_sid)); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Current User ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); - ok(ace->Mask == 0x1f01ff, "Current User ACE has unexpected mask (0x%lx != 0x1f01ff)\n", - ace->Mask); + todo_wine ok(ace->Mask == 0x1f01ff, + "Current User ACE has unexpected mask (0x%lx != 0x1f01ff)\n", ace->Mask); } if (acl_size.AceCount > 1) { bret = GetAce(pDacl, 1, (VOID **)&ace); ok(bret, "Failed to get Administators Group ACE.\n"); bret = EqualSid(&ace->SidStart, admin_sid); - todo_wine ok(bret, "Administators Group ACE (%s) != Administators Group SID (%s).\n", debugstr_sid(&ace->SidStart), debugstr_sid(admin_sid)); + ok(bret, "Administators Group ACE (%s) != Administators Group SID (%s).\n", debugstr_sid(&ace->SidStart), debugstr_sid(admin_sid)); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Administators Group ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); - ok(ace->Mask == 0x1f01ff, "Administators Group ACE has unexpected mask (0x%lx != 0x1f01ff)\n", - ace->Mask); + todo_wine ok(ace->Mask == 0x1f01ff, + "Administators Group ACE has unexpected mask (0x%lx != 0x1f01ff)\n", ace->Mask); } LocalFree(pSD); CloseHandle(obj); diff --git a/server/change.c b/server/change.c index f6d3d0252a9..9ffa8ab694f 100644 --- a/server/change.c +++ b/server/change.c @@ -1069,7 +1069,8 @@ static int dir_add_to_existing_notify( struct dir *dir ) #endif /* HAVE_SYS_INOTIFY_H */ -struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode ) +struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode, + const struct security_descriptor *sd ) { struct dir *dir; @@ -1089,6 +1090,11 @@ struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode ) dir->client_process = NULL; set_fd_user( fd, &dir_fd_ops, &dir->obj ); + if (sd) dir_set_sd( &dir->obj, sd, OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | + SACL_SECURITY_INFORMATION ); + dir_add_to_existing_notify( dir ); return &dir->obj; diff --git a/server/file.c b/server/file.c index 0b51b9d3a2f..2db89ae19f9 100644 --- a/server/file.c +++ b/server/file.c @@ -189,7 +189,8 @@ struct file *create_file_for_fd_obj( struct fd *fd, unsigned int access, unsigne return file; } -static struct object *create_file_obj( struct fd *fd, unsigned int access, mode_t mode ) +static struct object *create_file_obj( struct fd *fd, unsigned int access, mode_t mode, + const struct security_descriptor *sd ) { struct file *file = alloc_object( &file_ops ); @@ -201,6 +202,12 @@ static struct object *create_file_obj( struct fd *fd, unsigned int access, mode_ list_init( &file->kernel_object ); grab_object( fd ); set_fd_user( fd, &file_fd_ops, &file->obj ); + + if (sd) file_set_sd( &file->obj, sd, OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | + SACL_SECURITY_INFORMATION ); + return &file->obj; } @@ -273,11 +280,11 @@ static struct object *create_file( struct fd *root, const char *nameptr, data_si if (!fd) goto done; if (S_ISDIR(mode)) - obj = create_dir_obj( fd, access, mode ); + obj = create_dir_obj( fd, access, mode, sd ); else if (S_ISCHR(mode) && is_serial_fd( fd )) obj = create_serial( fd ); else - obj = create_file_obj( fd, access, mode ); + obj = create_file_obj( fd, access, mode, sd ); release_object( fd ); @@ -513,46 +520,66 @@ mode_t sd_to_mode( const struct security_descriptor *sd, const struct sid *owner int set_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid, const struct security_descriptor *sd, unsigned int set_info ) { + struct security_descriptor *new_sd; int unix_fd = get_unix_fd( fd ); - const struct sid *owner; + const struct sid *owner, *group; struct stat st; mode_t new_mode; - if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1; + if (!set_info || unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1; + if (!obj->sd) get_file_sd( obj, fd, mode, uid ); - if (set_info & OWNER_SECURITY_INFORMATION) - { - owner = sd_get_owner( sd ); - if (!owner) - { - set_error( STATUS_INVALID_SECURITY_DESCR ); - return 0; - } - if (!obj->sd || !equal_sid( owner, sd_get_owner( obj->sd ) )) - { - /* FIXME: get Unix uid and call fchown */ - } - } - else if (obj->sd) - owner = sd_get_owner( obj->sd ); - else - owner = token_get_owner( current->process->token ); + /* calculate the new sd, save to a temporary variable before assigning */ + new_sd = set_sd_from_token_internal( sd, obj->sd, set_info, current->process->token ); + if (new_sd) + { + if (set_info & OWNER_SECURITY_INFORMATION) + { + owner = sd_get_owner( new_sd ); + assert( owner ); - /* group and sacl not supported */ + if (!obj->sd || !equal_sid( owner, sd_get_owner( obj->sd ) )) + { + /* FIXME: get Unix uid and call fchown */ + } + } - if (set_info & DACL_SECURITY_INFORMATION) - { - /* keep the bits that we don't map to access rights in the ACL */ - new_mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); - new_mode |= sd_to_mode( sd, owner ); + if (set_info & GROUP_SECURITY_INFORMATION) + { + group = sd_get_group( new_sd ); + assert( group ); - if (((st.st_mode ^ new_mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, new_mode ) == -1) + if (!obj->sd || !equal_sid( group, sd_get_group( obj->sd ) )) + { + /* FIXME: get Unix uid and call fchown */ + } + } + + if (set_info & DACL_SECURITY_INFORMATION) { - file_set_error(); - return 0; - } - } - return 1; + owner = sd_get_owner( new_sd ); + assert( owner ); + + /* keep the bits that we don't map to access rights in the ACL */ + new_mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); + new_mode |= sd_to_mode( new_sd, owner ); + + if (((st.st_mode ^ new_mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, new_mode ) == -1) + { + free( new_sd ); + file_set_error(); + return 0; + } + + *mode = (*mode & S_IFMT) | new_mode; + } + + free( obj->sd ); + obj->sd = new_sd; + return 1; + } + + return 0; } static struct object *file_lookup_name( struct object *obj, struct unicode_str *name, @@ -692,7 +719,10 @@ DECL_HANDLER(create_file) if ((file = create_file( root_fd, name, name_len, nt_name, req->access, req->sharing, req->create, req->options, req->attrs, sd ))) { - reply->handle = alloc_handle( current->process, file, req->access, objattr->attributes ); + if (get_error() == STATUS_OBJECT_NAME_EXISTS) + reply->handle = alloc_handle( current->process, file, req->access, objattr->attributes ); + else + reply->handle = alloc_handle_no_access_check( current->process, file, req->access, objattr->attributes ); release_object( file ); } if (root_fd) release_object( root_fd ); diff --git a/server/file.h b/server/file.h index 796027fcb76..4f835ab1e99 100644 --- a/server/file.h +++ b/server/file.h @@ -217,7 +217,8 @@ extern struct object *create_unix_device( struct object *root, const struct unic extern void do_change_notify( int unix_fd ); extern void sigio_callback(void); -extern struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode ); +extern struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode, + const struct security_descriptor *sd ); extern struct dir *get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access ); /* completion */ From f4111043e6f2656cfee51cfdb5dbbf2f4edaf9fa Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Mon, 30 Mar 2015 13:04:23 +0200 Subject: [PATCH 612/758] server: Store file security attributes with extended file attributes. (v8) CW-Bug-Id: #22436 --- server/file.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/server/file.c b/server/file.c index 2db89ae19f9..6214858bca3 100644 --- a/server/file.c +++ b/server/file.c @@ -31,11 +31,21 @@ #include #include #include +#include #include #ifdef HAVE_UTIME_H #include #endif #include +#ifdef HAVE_ATTR_XATTR_H +#undef XATTR_ADDITIONAL_OPTIONS +#include +#elif defined(HAVE_SYS_XATTR_H) +#include +#endif +#ifdef HAVE_SYS_EXTATTR_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -63,6 +73,21 @@ struct type_descr file_type = }, }; +#ifndef XATTR_USER_PREFIX +#define XATTR_USER_PREFIX "user." +#endif +#ifndef XATTR_SIZE_MAX +#define XATTR_SIZE_MAX 65536 +#endif + +/* We intentionally do not match the Samba 4 extended attribute for NT security descriptors (SDs): + * 1) Samba stores this information using an internal data structure (we use a flat NT SD). + * 2) Samba uses the attribute "security.NTACL". This attribute is within a namespace that only + * the administrator has write access to, which prohibits the user from copying the attributes + * when copying a file and would require Wine to run with adminstrative privileges. + */ +#define WINE_XATTR_SD XATTR_USER_PREFIX "wine.sd" + struct file { struct object obj; /* object header */ @@ -217,6 +242,56 @@ int is_file_executable( const char *name ) return len >= 4 && (!strcasecmp( name + len - 4, ".exe") || !strcasecmp( name + len - 4, ".com" )); } +#ifdef HAVE_SYS_EXTATTR_H +static inline int xattr_valid_namespace( const char *name ) +{ + if (strncmp( XATTR_USER_PREFIX, name, XATTR_USER_PREFIX_LEN ) != 0) + { + errno = EPERM; + return 0; + } + return 1; +} +#endif + +static int xattr_fset( int filedes, const char *name, void *value, size_t size ) +{ +#if defined(XATTR_ADDITIONAL_OPTIONS) + return fsetxattr( filedes, name, value, size, 0, 0 ); +#elif defined(HAVE_SYS_XATTR_H) || defined(HAVE_ATTR_XATTR_H) + return fsetxattr( filedes, name, value, size, 0 ); +#elif defined(HAVE_SYS_EXTATTR_H) + if (!xattr_valid_namespace( name )) return -1; + return extattr_set_fd( filedes, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN], + value, size ); +#else + errno = ENOSYS; + return -1; +#endif +} + +static void set_xattr_sd( int fd, const struct security_descriptor *sd ) +{ + char buffer[XATTR_SIZE_MAX]; + int present, len; + const struct acl *dacl; + + /* there's no point in storing the security descriptor if there's no DACL */ + if (!sd) return; + dacl = sd_get_dacl( sd, &present ); + if (!present || !dacl) return; + + len = 2 + sizeof(struct security_descriptor) + sd->owner_len + + sd->group_len + sd->sacl_len + sd->dacl_len; + if (len > XATTR_SIZE_MAX) return; + + /* include the descriptor revision and resource manager control bits */ + buffer[0] = SECURITY_DESCRIPTOR_REVISION; + buffer[1] = 0; + memcpy( &buffer[2], sd, len - 2 ); + xattr_fset( fd, WINE_XATTR_SD, buffer, len ); +} + static struct object *create_file( struct fd *root, const char *nameptr, data_size_t len, struct unicode_str nt_name, unsigned int access, unsigned int sharing, int create, @@ -574,6 +649,9 @@ int set_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid, *mode = (*mode & S_IFMT) | new_mode; } + /* extended attributes are set after the file mode, to ensure it stays in sync */ + set_xattr_sd( unix_fd, new_sd ); + free( obj->sd ); obj->sd = new_sd; return 1; From a7a149e0803ee3fc336f1960159d9c2c87c6ec00 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Fri, 18 Apr 2014 14:05:32 -0600 Subject: [PATCH 613/758] server: Convert return of file security masks with generic access mappings. (try 7) CW-Bug-Id: #22436 --- dlls/advapi32/tests/security.c | 6 +++--- server/file.c | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index 05659e05264..07a682f7759 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -5024,8 +5024,8 @@ static void test_GetSecurityInfo(void) ok(bret, "Current User ACE (%s) != Current User SID (%s).\n", debugstr_sid(&ace->SidStart), debugstr_sid(user_sid)); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Current User ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); - todo_wine ok(ace->Mask == 0x1f01ff, - "Current User ACE has unexpected mask (0x%lx != 0x1f01ff)\n", ace->Mask); + ok(ace->Mask == 0x1f01ff, "Current User ACE has unexpected mask (0x%lx != 0x1f01ff)\n", + ace->Mask); } if (acl_size.AceCount > 1) { @@ -5035,7 +5035,7 @@ static void test_GetSecurityInfo(void) ok(bret, "Administators Group ACE (%s) != Administators Group SID (%s).\n", debugstr_sid(&ace->SidStart), debugstr_sid(admin_sid)); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Administators Group ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); - todo_wine ok(ace->Mask == 0x1f01ff, + ok(ace->Mask == 0x1f01ff, "Administators Group ACE has unexpected mask (0x%lx != 0x1f01ff)\n", ace->Mask); } LocalFree(pSD); diff --git a/server/file.c b/server/file.c index 6214858bca3..fe1d09ed819 100644 --- a/server/file.c +++ b/server/file.c @@ -472,6 +472,26 @@ struct security_descriptor *mode_to_sd( mode_t mode, const struct sid *user, con return sd; } +/* Convert generic rights into standard access rights */ +static void convert_generic_sd( struct security_descriptor *sd ) +{ + const struct acl *dacl; + int present; + + dacl = sd_get_dacl( sd, &present ); + if (present && dacl) + { + const struct ace *ace = (const struct ace *)(dacl + 1); + ULONG i; + + for (i = 0; i < dacl->count; i++, ace = ace_next( ace )) + { + DWORD *mask = (DWORD *)(ace + 1); + *mask = map_access( *mask, &file_type.mapping ); + } + } +} + struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid ) { @@ -608,6 +628,9 @@ int set_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid, new_sd = set_sd_from_token_internal( sd, obj->sd, set_info, current->process->token ); if (new_sd) { + /* convert generic rights into standard access rights */ + convert_generic_sd( new_sd ); + if (set_info & OWNER_SECURITY_INFORMATION) { owner = sd_get_owner( new_sd ); From 8357bc5a6e255aa3747e52508f7d15dc5629b158 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Fri, 18 Apr 2014 14:01:35 -0600 Subject: [PATCH 614/758] server: Retrieve file security attributes with extended file attributes. (try 7) CW-Bug-Id: #22436 --- dlls/advapi32/tests/security.c | 19 ++++++------- server/file.c | 50 ++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index 07a682f7759..5dfddf8ddde 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -3728,7 +3728,7 @@ static void test_CreateDirectoryA(void) } ok(!error, "GetNamedSecurityInfo failed with error %ld\n", error); test_inherited_dacl(pDacl, admin_sid, user_sid, OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE, - 0x1f01ff, FALSE, TRUE, FALSE, __LINE__); + 0x1f01ff, FALSE, FALSE, FALSE, __LINE__); LocalFree(pSD); /* Test inheritance of ACLs in CreateFile without security descriptor */ @@ -4182,21 +4182,20 @@ static void test_GetNamedSecurityInfoA(void) bret = GetAce(pDacl, 0, (VOID **)&ace); ok(bret, "Failed to get Current User ACE.\n"); bret = EqualSid(&ace->SidStart, user_sid); - todo_wine ok(bret, "Current User ACE (%s) != Current User SID (%s).\n", - debugstr_sid(&ace->SidStart), debugstr_sid(user_sid)); + ok(bret, "Current User ACE (%s) != Current User SID (%s).\n", + debugstr_sid(&ace->SidStart), debugstr_sid(user_sid)); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Current User ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); - ok(ace->Mask == 0x1f01ff, "Current User ACE has unexpected mask (0x%lx != 0x1f01ff)\n", - ace->Mask); + ok(ace->Mask == 0x1f01ff, + "Current User ACE has unexpected mask (0x%lx != 0x1f01ff)\n", ace->Mask); } if (acl_size.AceCount > 1) { bret = GetAce(pDacl, 1, (VOID **)&ace); ok(bret, "Failed to get Administators Group ACE.\n"); bret = EqualSid(&ace->SidStart, admin_sid); - todo_wine ok(bret || broken(!bret) /* win2k */, - "Administators Group ACE (%s) != Administators Group SID (%s).\n", - debugstr_sid(&ace->SidStart), debugstr_sid(admin_sid)); + ok(bret || broken(!bret) /* win2k */, "Administators Group ACE (%s) != Administators Group SID (%s).\n", + debugstr_sid(&ace->SidStart), debugstr_sid(admin_sid)); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Administators Group ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff || broken(ace->Mask == GENERIC_ALL) /* win2k */, @@ -4223,8 +4222,8 @@ static void test_GetNamedSecurityInfoA(void) { bret = GetAce(pDacl, 0, (VOID **)&ace); ok(bret, "Failed to get ACE.\n"); - todo_wine ok(((ACE_HEADER *)ace)->AceFlags & INHERITED_ACE, - "ACE has unexpected flags: 0x%x\n", ((ACE_HEADER *)ace)->AceFlags); + ok(((ACE_HEADER *)ace)->AceFlags & INHERITED_ACE, + "ACE has unexpected flags: 0x%x\n", ((ACE_HEADER *)ace)->AceFlags); } LocalFree(pSD); diff --git a/server/file.c b/server/file.c index fe1d09ed819..6da354af245 100644 --- a/server/file.c +++ b/server/file.c @@ -44,6 +44,7 @@ #include #endif #ifdef HAVE_SYS_EXTATTR_H +#undef XATTR_ADDITIONAL_OPTIONS #include #endif @@ -76,6 +77,9 @@ struct type_descr file_type = #ifndef XATTR_USER_PREFIX #define XATTR_USER_PREFIX "user." #endif +#ifndef XATTR_USER_PREFIX_LEN +#define XATTR_USER_PREFIX_LEN (sizeof(XATTR_USER_PREFIX) - 1) +#endif #ifndef XATTR_SIZE_MAX #define XATTR_SIZE_MAX 65536 #endif @@ -254,6 +258,22 @@ static inline int xattr_valid_namespace( const char *name ) } #endif +static int xattr_fget( int filedes, const char *name, void *value, size_t size ) +{ +#if defined(XATTR_ADDITIONAL_OPTIONS) + return fgetxattr( filedes, name, value, size, 0, 0 ); +#elif defined(HAVE_SYS_XATTR_H) || defined(HAVE_ATTR_XATTR_H) + return fgetxattr( filedes, name, value, size ); +#elif defined(HAVE_SYS_EXTATTR_H) + if (!xattr_valid_namespace( name )) return -1; + return extattr_get_fd( filedes, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN], + value, size ); +#else + errno = ENOSYS; + return -1; +#endif +} + static int xattr_fset( int filedes, const char *name, void *value, size_t size ) { #if defined(XATTR_ADDITIONAL_OPTIONS) @@ -492,6 +512,29 @@ static void convert_generic_sd( struct security_descriptor *sd ) } } +static struct security_descriptor *get_xattr_sd( int fd ) +{ + struct security_descriptor *sd; + char buffer[XATTR_SIZE_MAX]; + int n; + + n = xattr_fget( fd, WINE_XATTR_SD, buffer, sizeof(buffer) ); + if (n == -1 || n < 2 + sizeof(struct security_descriptor)) return NULL; + + /* validate that we can handle the descriptor */ + if (buffer[0] != SECURITY_DESCRIPTOR_REVISION || buffer[1] != 0 || + !sd_is_valid( (struct security_descriptor *)&buffer[2], n - 2 )) + return NULL; + + sd = mem_alloc( n - 2 ); + if (sd) + { + memcpy( sd, &buffer[2], n - 2 ); + convert_generic_sd( sd ); /* for backwards compatibility */ + } + return sd; +} + struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid ) { @@ -507,9 +550,10 @@ struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode (st.st_uid == *uid)) return obj->sd; - sd = mode_to_sd( st.st_mode, - security_unix_uid_to_sid( st.st_uid ), - token_get_primary_group( current->process->token )); + sd = get_xattr_sd( unix_fd ); + if (!sd) sd = mode_to_sd( st.st_mode, + security_unix_uid_to_sid( st.st_uid ), + token_get_primary_group( current->process->token )); if (!sd) return obj->sd; *mode = st.st_mode; From c72ac55e4b552ed966820281fbac4de56985e537 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 21 Jul 2023 19:24:45 -0600 Subject: [PATCH 615/758] ntdll: Match Windows used block filling. CW-Bug-Id: #21739 --- dlls/kernel32/tests/heap.c | 41 ++++++++++++++++++++++++++++++++++++++ dlls/ntdll/heap.c | 6 ++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/dlls/kernel32/tests/heap.c b/dlls/kernel32/tests/heap.c index 59a82c0c579..a1d72302098 100644 --- a/dlls/kernel32/tests/heap.c +++ b/dlls/kernel32/tests/heap.c @@ -3270,8 +3270,49 @@ static void test_heap_checks( DWORD flags ) ret = HeapFree( GetProcessHeap(), 0, p ); ok( ret, "HeapFree failed\n" ); + if (flags & HEAP_FREE_CHECKING_ENABLED) + { + void *p_prev; + SIZE_T count; + + p = pHeapAlloc( GetProcessHeap(), 0, 39 ); + ok( !!p, "HeapAlloc failed\n" ); + for (i = 0; i < 39 / sizeof(int); ++i) + ok( ((unsigned int *)p)[i] == 0xbaadf00d, "got %#x, i %Iu.\n", ((unsigned int *)p)[i], i ); + memset( p, 0xcc, 39 ); + + p_prev = p; + count = 5; + p = pHeapReAlloc( GetProcessHeap(), 0, p, 39 + count * 4 ); + ok( !!p, "got NULL.\n" ); + + winetest_push_context( "in place %d", p == p_prev); + if (p == p_prev) + { + if (flags & HEAP_TAIL_CHECKING_ENABLED) + ok( p[39] == 0xab, "got %#x.\n", p[39] ); + for (i = 0; i < count - 1; ++i) + ok( ((unsigned int *)p)[10 + i] == 0xbaadf00d, "got %#x, i %Iu.\n", ((unsigned int *)p)[10 + i], i ); + } + else + { + ok( p[39] == 0xba, "got %#x.\n", p[39] ); + for (i = 0; i < count - 1; ++i) + ok( ((unsigned int *)p)[10 + i] == 0xbaadf00d, "got %#x, i %Iu.\n", ((unsigned int *)p)[10 + i], i ); + } + winetest_pop_context(); + + ret = pHeapFree( GetProcessHeap(), 0, p ); + ok( ret, "failed.\n" ); + } + p = HeapAlloc( GetProcessHeap(), 0, 37 ); ok( p != NULL, "HeapAlloc failed\n" ); + if (flags & HEAP_FREE_CHECKING_ENABLED) + { + for (i = 0; i < 37 / sizeof(int); ++i) + ok( ((unsigned int *)p)[i] == 0xbaadf00d, "got %#x, i %Iu.\n", ((unsigned int *)p)[i], i ); + } memset( p, 0xcc, 37 ); ret = pHeapFree( GetProcessHeap(), 0, p ); diff --git a/dlls/ntdll/heap.c b/dlls/ntdll/heap.c index 5a419f6ac98..9c1ccb559c0 100644 --- a/dlls/ntdll/heap.c +++ b/dlls/ntdll/heap.c @@ -140,7 +140,7 @@ C_ASSERT( sizeof(ARENA_LARGE) == 4 * BLOCK_ALIGN ); #define BLOCK_TYPE_FREE 'F' #define BLOCK_TYPE_LARGE 'L' -#define BLOCK_FILL_USED 0x55 +#define BLOCK_FILL_USED 0xbaadf00d #define BLOCK_FILL_TAIL 0xab #define BLOCK_FILL_FREE 0xfeeefeee @@ -516,6 +516,7 @@ static inline void mark_block_tail( struct block *block, DWORD flags ) static inline void initialize_block( struct block *block, SIZE_T old_size, SIZE_T size, DWORD flags ) { char *data = (char *)(block + 1); + SIZE_T i; if (size <= old_size) return; @@ -527,7 +528,8 @@ static inline void initialize_block( struct block *block, SIZE_T old_size, SIZE_ else if (flags & HEAP_FREE_CHECKING_ENABLED) { valgrind_make_writable( data + old_size, size - old_size ); - memset( data + old_size, BLOCK_FILL_USED, size - old_size ); + i = ROUND_SIZE( old_size, sizeof(DWORD) - 1 ) / sizeof(DWORD); + for (; i < size / sizeof(DWORD); ++i) ((DWORD *)data)[i] = BLOCK_FILL_USED; } } From d132c1520b97afda717d8e6c0900bb5ec246bb0f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 21 Jul 2023 21:09:41 -0600 Subject: [PATCH 616/758] wine.inf: Set FLG_HEAP_ENABLE_FREE_CHECK for Chaos Code. CW-Bug-Id: #21739 --- loader/wine.inf.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index d642cadfe2e..1e8c569a950 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -418,6 +418,8 @@ HKLM,%CurrentVersionNT%\Ports,,16 HKLM,%CurrentVersionNT%\Print,,16 HKLM,%CurrentVersionNT%\ProfileList,,16 HKLM,%CurrentVersionNT%\Winlogon,"Shell",,"explorer.exe" +;; App specific heap debug flags +HKLM,%CurrentVersionNT%\Image File Execution Options\ChaosCode.exe,GlobalFlag,0x00040002,0x00000020 [CurrentVersionWow64] HKLM,%CurrentVersion%,"ProgramFilesDir (x86)",,"%16426%" From 41b8326175f8547d6e5501d0235d84dc117168af Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 22 Jul 2023 14:47:13 -0400 Subject: [PATCH 617/758] Revert "sapi: ISpObjectToken CreateInstance support ISpAudio" This reverts commit b9f13c5efd15621354e4ebd02c5164a24d5b6047. CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/token.c | 279 +--------------------------------------------- 1 file changed, 1 insertion(+), 278 deletions(-) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 4e44ba8a354..385555a4cef 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1166,290 +1166,13 @@ static HRESULT WINAPI token_GetCategory( ISpObjectToken *iface, return E_NOTIMPL; } -struct speech_audio -{ - ISpAudio ISpAudio_iface; - LONG ref; -}; - -static inline struct speech_audio *impl_from_ISpAudio(ISpAudio *iface) -{ - return CONTAINING_RECORD(iface, struct speech_audio, ISpAudio_iface); -} - -static HRESULT WINAPI spaudio_QueryInterface(ISpAudio *iface, REFIID iid, void **obj) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - - TRACE("(%p, %s %p).\n", audio, debugstr_guid(iid), obj); - - if (IsEqualIID(iid, &IID_IUnknown) || - IsEqualIID(iid, &IID_ISequentialStream) || - IsEqualIID(iid, &IID_IStream) || - IsEqualIID(iid, &IID_ISpStreamFormat) || - IsEqualIID(iid, &IID_ISpAudio)) - *obj = &audio->ISpAudio_iface; - else - { - *obj = NULL; - FIXME("interface %s not implemented.\n", debugstr_guid(iid)); - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown *)*obj); - return S_OK; -} - -static ULONG WINAPI spaudio_AddRef(ISpAudio *iface) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - ULONG ref = InterlockedIncrement(&audio->ref); - - TRACE("(%p): ref=%lu.\n", audio, ref); - - return ref; -} - -static ULONG WINAPI spaudio_Release(ISpAudio *iface) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - ULONG ref = InterlockedDecrement(&audio->ref); - - TRACE("(%p): ref=%lu.\n", audio, ref); - - if (!ref) - { - heap_free(audio); - } - - return ref; -} - -static HRESULT WINAPI spaudio_Read(ISpAudio *iface,void *pv, ULONG cb, ULONG *pcbRead) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - - FIXME("%p, %p, %ld %p\n", audio, pv, cb, pcbRead); - - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_Write(ISpAudio *iface, const void *pv, ULONG cb, ULONG *pcbWritten) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - - FIXME("%p, %p, %ld %p\n", audio, pv, cb, pcbWritten); - - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_Seek(ISpAudio *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %s, %ld, %p\n", audio, wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_SetSize(ISpAudio *iface, ULARGE_INTEGER libNewSize) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("(%p, %s)\n", audio, wine_dbgstr_longlong(libNewSize.QuadPart)); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_CopyTo(ISpAudio *iface,IStream *pstm, ULARGE_INTEGER cb, - ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("(%p, %p, %s, %p, %p)\n", audio, pstm, wine_dbgstr_longlong(cb.QuadPart), pcbRead, pcbWritten); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_Commit(ISpAudio *iface,DWORD grfCommitFlags) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("(%p, %#lx)\n", audio, grfCommitFlags); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_Revert(ISpAudio *iface) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("(%p)\n", audio); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_LockRegion(ISpAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("(%p, %s, %s, %ld)\n", audio, wine_dbgstr_longlong(offset.QuadPart), - wine_dbgstr_longlong(cb.QuadPart), dwLockType); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_UnlockRegion(ISpAudio *iface,ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD dwLockType) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("(%p, %s, %s, %ld)\n", audio, wine_dbgstr_longlong(offset.QuadPart), - wine_dbgstr_longlong(cb.QuadPart), dwLockType); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_Stat(ISpAudio *iface, STATSTG *stg, DWORD flag) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p, %ld\n", audio, stg, flag); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_Clone(ISpAudio *iface, IStream **ppstm) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p\n", audio, ppstm); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_GetFormat(ISpAudio *iface, GUID *format, WAVEFORMATEX **wave) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p, %p\n", audio, format, wave); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_SetState(ISpAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %d, %s\n", audio, state, wine_dbgstr_longlong(reserved)); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_SetFormat(ISpAudio *iface, REFGUID guid, const WAVEFORMATEX *wave) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %s, %p\n", audio, debugstr_guid(guid), wave); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_GetStatus(ISpAudio *iface, SPAUDIOSTATUS *status) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p\n", audio, status); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_SetBufferInfo(ISpAudio *iface,const SPAUDIOBUFFERINFO *buffer) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p\n", audio, buffer); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_GetBufferInfo(ISpAudio *iface, SPAUDIOBUFFERINFO *buffer) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p\n", audio, buffer); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_GetDefaultFormat(ISpAudio *iface, GUID *guid, WAVEFORMATEX **wave) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p, %p\n", audio, guid, wave); - return E_NOTIMPL; -} - -static HANDLE WINAPI spaudio_EventHandle(ISpAudio *iface) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p\n", audio); - return NULL; -} - -static HRESULT WINAPI spaudio_GetVolumeLevel(ISpAudio *iface, ULONG *level) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p\n", audio, level); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_SetVolumeLevel(ISpAudio *iface, ULONG level) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %ld\n", audio, level); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_GetBufferNotifySize(ISpAudio *iface, ULONG *size) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %p\n", audio, size); - return E_NOTIMPL; -} - -static HRESULT WINAPI spaudio_SetBufferNotifySize(ISpAudio *iface, ULONG size) -{ - struct speech_audio *audio = impl_from_ISpAudio(iface); - FIXME("%p, %ld\n", audio, size); - return E_NOTIMPL; -} - -const struct ISpAudioVtbl spaudio_vtbl = -{ - spaudio_QueryInterface, - spaudio_AddRef, - spaudio_Release, - spaudio_Read, - spaudio_Write, - spaudio_Seek, - spaudio_SetSize, - spaudio_CopyTo, - spaudio_Commit, - spaudio_Revert, - spaudio_LockRegion, - spaudio_UnlockRegion, - spaudio_Stat, - spaudio_Clone, - spaudio_GetFormat, - spaudio_SetState, - spaudio_SetFormat, - spaudio_GetStatus, - spaudio_SetBufferInfo, - spaudio_GetBufferInfo, - spaudio_GetDefaultFormat, - spaudio_EventHandle, - spaudio_GetVolumeLevel, - spaudio_SetVolumeLevel, - spaudio_GetBufferNotifySize, - spaudio_SetBufferNotifySize -}; - -static HRESULT speech_audio_create(void **obj) -{ - struct speech_audio *This = heap_alloc(sizeof(*This)); - - if (!This) - return E_OUTOFMEMORY; - This->ISpAudio_iface.lpVtbl = &spaudio_vtbl; - This->ref = 1; - - *obj = &This->ISpAudio_iface; - return S_OK; -} - static HRESULT WINAPI token_CreateInstance( ISpObjectToken *iface, IUnknown *outer, DWORD class_context, REFIID riid, void **object ) { - struct object_token *This = impl_from_ISpObjectToken( iface ); - - FIXME( "(%p)->(%p 0x%08lx %s, %p): semi-stub\n", This, outer, class_context, debugstr_guid( riid ), object); - - if (IsEqualIID(riid, &IID_ISpAudio)) - { - return speech_audio_create(object); - } + FIXME( "stub\n" ); return E_NOTIMPL; } From 961ac73aaf5605cd92d1b52ab0e554e7d555bf84 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 25 Apr 2023 19:15:59 -0400 Subject: [PATCH 618/758] include: Add more sapi structs and enums. (cherry picked from commit 5f977d2fa878297748839eb767857d5a84954688) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- include/sapi.idl | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/include/sapi.idl b/include/sapi.idl index cd5d6044bdf..92a2881501c 100644 --- a/include/sapi.idl +++ b/include/sapi.idl @@ -574,6 +574,59 @@ typedef [restricted, hidden] struct SPAUDIOBUFFERINFO ULONG ulMsEventBias; } SPAUDIOBUFFERINFO; +typedef [hidden] enum SPPARTOFSPEECH +{ + SPPS_NotOverriden = -1, + SPPS_Unknown = 0, + SPPS_Noun = 0x1000, + SPPS_Verb = 0x2000, + SPPS_Modifier = 0x3000, + SPPS_Function = 0x4000, + SPPS_Interjection = 0x5000, + SPPS_Noncontent = 0x6000, + SPPS_LMA = 0x7000, + SPPS_SuppressWord = 0xF000 +} SPPARTOFSPEECH; + +typedef [restricted, hidden] struct SPVPITCH +{ + long MiddleAdj; + long RangeAdj; +} SPVPITCH; + +typedef [hidden] enum SPVACTIONS +{ + SPVA_Speak = 0, + SPVA_Silence, + SPVA_Pronounce, + SPVA_Bookmark, + SPVA_SpellOut, + SPVA_Section, + SPVA_ParseUnknownTag +} SPVACTIONS; + +typedef [restricted, hidden] struct SPVCONTEXT +{ + LPCWSTR pCategory; + LPCWSTR pBefore; + LPCWSTR pAfter; +} SPVCONTEXT; + +typedef [restricted, hidden] struct SPVSTATE +{ + SPVACTIONS eAction; + LANGID LangID; + WORD wReserved; + long EmphAdj; + long RateAdj; + ULONG Volume; + SPVPITCH PitchAdj; + ULONG SilenceMSecs; + SPPHONEID *pPhoneIds; + SPPARTOFSPEECH ePartOfSpeech; + SPVCONTEXT Context; +} SPVSTATE; + cpp_quote("#if defined(__GNUC__)") cpp_quote("#define SPCAT_AUDIOOUT (const WCHAR []){ 'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E','\\\\','S','O','F','T','W','A','R','E','\\\\','M','i','c','r','o','s','o','f','t','\\\\','S','p','e','e','c','h','\\\\','A','u','d','i','o','O','u','t','p','u','t',0 }") From ee623ba7ceac7c3c5e9b0f49c5e433ecebcbaddf Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 25 Apr 2023 19:17:50 -0400 Subject: [PATCH 619/758] include: Add ISpTTSEngineSite and ISpTTSEngine interfaces. (cherry picked from commit 7f1df4b27dde3d79a6d5f0b709c1e5c32582b20b) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- include/sapiddk.idl | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/include/sapiddk.idl b/include/sapiddk.idl index 670b8c0dce5..46aebbb9a12 100644 --- a/include/sapiddk.idl +++ b/include/sapiddk.idl @@ -49,6 +49,69 @@ interface ISpObjectTokenEnumBuilder : IEnumSpObjectTokens HRESULT Sort([in] LPCWSTR pszTokenIdToListFirst); } +typedef enum SPVSKIPTYPE +{ + SPVST_SENTENCE = (1L << 0) +} SPVSKIPTYPE; + +typedef enum SPVESACTIONS +{ + SPVES_CONTINUE = 0, + SPVES_ABORT = (1L << 0), + SPVES_SKIP = (1L << 1), + SPVES_RATE = (1L << 2), + SPVES_VOLUME = (1L << 3) +} SPVESACTIONS; + +[ + object, + uuid(9880499b-cce9-11d2-b503-00c04f797396), + helpstring("ISpTTSEngineSite"), + pointer_default(unique), + local +] +interface ISpTTSEngineSite : ISpEventSink +{ + HRESULT GetActions(); + HRESULT Write([in] const void *pBuff, + [in] ULONG cb, + [out] ULONG *pcbWritten); + HRESULT GetRate([out] long *pRateAdjust); + HRESULT GetVolume([out] USHORT *pusVolume); + HRESULT GetSkipInfo([out] SPVSKIPTYPE *peType, + [out] long *plNumItems); + HRESULT CompleteSkip([in] long lNumSkipped); +}; + +typedef struct SPVTEXTFRAG +{ + struct SPVTEXTFRAG* pNext; + SPVSTATE State; + LPCWSTR pTextStart; + ULONG ulTextLen; + ULONG ulTextSrcOffset; +} SPVTEXTFRAG; + +[ + object, + uuid(a74d7c8e-4cc5-4f2f-a6eb-804dee18500e), + helpstring("ISpTTSEngine"), + pointer_default(unique), + local +] +interface ISpTTSEngine : IUnknown +{ + HRESULT Speak([in] DWORD dwSpeakFlags, + [in] REFGUID rguidFormatId, + [in] const WAVEFORMATEX *pWaveFormatEx, + [in] const SPVTEXTFRAG *pTextFragList, + [in] ISpTTSEngineSite *pOutputSite); + HRESULT GetOutputFormat([in] const GUID *pTargetFmtId, + [in] const WAVEFORMATEX *pTargetWaveFormatEx, + [out] GUID *pOutputFormatId, + [out] WAVEFORMATEX **ppCoMemOutputWaveFormatEx); +}; + [ helpstring("Speech Object DDK Library"), uuid(9903f14c-12ce-4c99-9986-2ee3d7d588a8), From ebeb9c72c4d8652ab8fe0c9f3c61d35c99d2ee6b Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:11:29 -0400 Subject: [PATCH 620/758] sapi: Implement ISpRegDataKey::SetStringValue. (cherry picked from commit 762918c2ecc9d11c9d54a9f1147f6f1241017e40) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 14 +++++++++++++- dlls/sapi/token.c | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 958c50359f5..792b65a6366 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -33,7 +33,7 @@ static void test_data_key(void) HRESULT hr; HKEY key; LONG res; - WCHAR *value; + WCHAR *value = NULL; hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, &IID_ISpRegDataKey, (void **)&data_key ); @@ -49,6 +49,9 @@ static void test_data_key(void) hr = ISpRegDataKey_GetStringValue( data_key, L"Voice", &value ); ok( hr == E_HANDLE, "got %08lx\n", hr ); + hr = ISpRegDataKey_SetStringValue( data_key, L"Voice", L"Test" ); + ok( hr == E_HANDLE, "got %08lx\n", hr ); + hr = ISpRegDataKey_SetKey( data_key, key, FALSE ); ok( hr == S_OK, "got %08lx\n", hr ); hr = ISpRegDataKey_SetKey( data_key, key, FALSE ); @@ -60,6 +63,14 @@ static void test_data_key(void) hr = ISpRegDataKey_GetStringValue( data_key, L"", &value ); ok( hr == SPERR_NOT_FOUND, "got %08lx\n", hr ); + hr = ISpRegDataKey_SetStringValue( data_key, L"Voice", L"Test" ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpRegDataKey_GetStringValue( data_key, L"Voice", &value ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( !wcscmp( value, L"Test" ), "got %s\n", wine_dbgstr_w(value) ); + CoTaskMemFree( value ); + hr = ISpRegDataKey_CreateKey( data_key, L"Testing", &sub ); ok( hr == S_OK, "got %08lx\n", hr ); ISpDataKey_Release(sub); @@ -361,6 +372,7 @@ static void test_object_token(void) START_TEST(token) { CoInitialize( NULL ); + RegDeleteTreeA( HKEY_CURRENT_USER, "Software\\Winetest\\sapi" ); test_data_key(); test_token_category(); test_token_enum(); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 385555a4cef..6c862bb030c 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -124,8 +124,18 @@ static HRESULT WINAPI data_key_GetData( ISpRegDataKey *iface, LPCWSTR name, static HRESULT WINAPI data_key_SetStringValue( ISpRegDataKey *iface, LPCWSTR name, LPCWSTR value ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct data_key *This = impl_from_ISpRegDataKey( iface ); + DWORD ret, size; + + TRACE( "%p, %s, %s\n", This, debugstr_w(name), debugstr_w(value) ); + + if (!This->key) + return E_HANDLE; + + size = (wcslen(value) + 1) * sizeof(WCHAR); + ret = RegSetValueExW( This->key, name, 0, REG_SZ, (BYTE *)value, size ); + + return HRESULT_FROM_WIN32(ret); } static HRESULT WINAPI data_key_GetStringValue( ISpRegDataKey *iface, From fd3a928bfcbc5b5cdd07df672ec5926e7cb9a6fd Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 12:34:13 -0400 Subject: [PATCH 621/758] sapi: Ignore read_only in ISpRegDataKey::SetKey. (cherry picked from commit 51e84c02e72319bf2532b3c2b1e320abd1f6ca54) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 24 ++++++++++++++++++++++++ dlls/sapi/token.c | 4 +--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 792b65a6366..603e2143ff9 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -76,6 +76,30 @@ static void test_data_key(void) ISpDataKey_Release(sub); ISpRegDataKey_Release( data_key ); + + hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpRegDataKey, (void **)&data_key ); + ok( hr == S_OK, "got %08lx\n", hr ); + + res = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\\Winetest\\sapi", 0, KEY_ALL_ACCESS, &key ); + ok( res == ERROR_SUCCESS, "got %ld\n", res ); + + hr = ISpRegDataKey_SetKey( data_key, key, TRUE ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpRegDataKey_SetStringValue( data_key, L"Voice2", L"Test2" ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpRegDataKey_GetStringValue( data_key, L"Voice2", &value ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( !wcscmp( value, L"Test2" ), "got %s\n", wine_dbgstr_w(value) ); + CoTaskMemFree( value ); + + hr = ISpRegDataKey_CreateKey( data_key, L"Testing2", &sub ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpDataKey_Release(sub); + + ISpRegDataKey_Release( data_key ); } static void test_token_category(void) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 6c862bb030c..74ef6884fbc 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -40,7 +40,6 @@ struct data_key LONG ref; HKEY key; - BOOL read_only; }; static struct data_key *impl_from_ISpRegDataKey( ISpRegDataKey *iface ) @@ -253,8 +252,8 @@ static HRESULT WINAPI data_key_SetKey( ISpRegDataKey *iface, if (This->key) return SPERR_ALREADY_INITIALIZED; + /* read_only is ignored in Windows implementations. */ This->key = key; - This->read_only = read_only; return S_OK; } @@ -287,7 +286,6 @@ HRESULT data_key_create( IUnknown *outer, REFIID iid, void **obj ) This->ISpRegDataKey_iface.lpVtbl = &data_key_vtbl; This->ref = 1; This->key = NULL; - This->read_only = FALSE; hr = ISpRegDataKey_QueryInterface( &This->ISpRegDataKey_iface, iid, obj ); From befdba680c8e44557abb3873f0ef9cc59fe3182b Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:17:23 -0400 Subject: [PATCH 622/758] sapi: Implement ISpRegDataKey::OpenKey. (cherry picked from commit 3bba4065d3af9868c56afe3046220dd11592a5d9) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 7 +++++++ dlls/sapi/token.c | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 603e2143ff9..2602b043980 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -71,10 +71,17 @@ static void test_data_key(void) ok( !wcscmp( value, L"Test" ), "got %s\n", wine_dbgstr_w(value) ); CoTaskMemFree( value ); + hr = ISpRegDataKey_OpenKey( data_key, L"Testing", &sub ); + ok( hr == SPERR_NOT_FOUND, "got %08lx\n", hr ); + hr = ISpRegDataKey_CreateKey( data_key, L"Testing", &sub ); ok( hr == S_OK, "got %08lx\n", hr ); ISpDataKey_Release(sub); + hr = ISpRegDataKey_OpenKey( data_key, L"Testing", &sub ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpDataKey_Release(sub); + ISpRegDataKey_Release( data_key ); hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 74ef6884fbc..7bd28dca31c 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -186,8 +186,37 @@ static HRESULT WINAPI data_key_GetDWORD( ISpRegDataKey *iface, static HRESULT WINAPI data_key_OpenKey( ISpRegDataKey *iface, LPCWSTR name, ISpDataKey **sub_key ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct data_key *This = impl_from_ISpRegDataKey( iface ); + ISpRegDataKey *spregkey; + HRESULT hr; + HKEY key; + LONG ret; + + TRACE( "%p, %s, %p\n", This, debugstr_w(name), sub_key ); + + ret = RegOpenKeyExW( This->key, name, 0, KEY_ALL_ACCESS, &key ); + if (ret != ERROR_SUCCESS) + return SPERR_NOT_FOUND; + + hr = data_key_create( NULL, &IID_ISpRegDataKey, (void**)&spregkey ); + if (FAILED(hr)) + { + RegCloseKey( key ); + return hr; + } + + hr = ISpRegDataKey_SetKey( spregkey, key, FALSE ); + if (FAILED(hr)) + { + RegCloseKey( key ); + ISpRegDataKey_Release( spregkey ); + return hr; + } + + hr = ISpRegDataKey_QueryInterface( spregkey, &IID_ISpDataKey, (void**)sub_key ); + ISpRegDataKey_Release( spregkey ); + + return hr; } static HRESULT WINAPI data_key_CreateKey( ISpRegDataKey *iface, From 8ed8d6c12cb3bb816804ef4e92d345dbfaf9f827 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:23:35 -0400 Subject: [PATCH 623/758] sapi: Use ISpRegDataKey in object_token. (cherry picked from commit f6c1a7444ccb96c4990f55a6a42e5d3fb0d05534) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/token.c | 102 ++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 7bd28dca31c..0465819de56 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -52,7 +52,7 @@ struct object_token ISpObjectToken ISpObjectToken_iface; LONG ref; - HKEY token_key; + ISpRegDataKey *data_key; WCHAR *token_id; }; @@ -496,6 +496,23 @@ static HRESULT parse_cat_id( const WCHAR *str, HKEY *root, const WCHAR **sub_key return S_FALSE; } +static HRESULT WINAPI create_data_key_with_hkey( HKEY key, ISpRegDataKey **data_key ) +{ + HRESULT hr; + + if (FAILED(hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpRegDataKey, (void **)data_key ) )) + return hr; + + if (FAILED(hr = ISpRegDataKey_SetKey( *data_key, key, TRUE ))) + { + ISpRegDataKey_Release( *data_key ); + *data_key = NULL; + } + + return hr; +} + static HRESULT WINAPI token_category_SetId( ISpObjectTokenCategory *iface, LPCWSTR id, BOOL create ) { @@ -518,17 +535,12 @@ static HRESULT WINAPI token_category_SetId( ISpObjectTokenCategory *iface, res = RegOpenKeyExW( root, subkey, 0, KEY_ALL_ACCESS, &key ); if (res) return SPERR_INVALID_REGISTRY_KEY; - hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_ALL, - &IID_ISpRegDataKey, (void **)&This->data_key ); - if (FAILED(hr)) goto fail; - - hr = ISpRegDataKey_SetKey( This->data_key, key, FALSE ); - if (FAILED(hr)) goto fail; - - return hr; + if (FAILED(hr = create_data_key_with_hkey( key, &This->data_key ))) + { + RegCloseKey( key ); + return hr; + } -fail: - RegCloseKey( key ); return hr; } @@ -748,6 +760,7 @@ static HRESULT WINAPI token_enum_Next( ISpObjectTokenEnumBuilder *iface, WCHAR *subkey_name; HKEY sub_key; DWORD size; + ISpRegDataKey *data_key; TRACE( "(%p)->(%lu %p %p)\n", This, num, tokens, fetched ); @@ -777,15 +790,24 @@ static HRESULT WINAPI token_enum_Next( ISpObjectTokenEnumBuilder *iface, return E_FAIL; } + hr = create_data_key_with_hkey( sub_key, &data_key ); + if (FAILED(hr)) + { + free( subkey_name ); + RegCloseKey( sub_key ); + return hr; + } + hr = token_create( NULL, &IID_ISpObjectToken, (void**)tokens ); if (FAILED(hr)) { free( subkey_name ); + ISpRegDataKey_Release( data_key ); return hr; } object = impl_from_ISpObjectToken( *tokens ); - object->token_key = sub_key; + object->data_key = data_key; object->token_id = subkey_name; if (fetched) *fetched = 1; @@ -823,6 +845,7 @@ static HRESULT WINAPI token_enum_Item( ISpObjectTokenEnumBuilder *iface, DWORD size; LONG ret; HKEY key; + ISpRegDataKey *data_key; TRACE( "%p, %lu, %p\n", This, index, token ); @@ -849,15 +872,24 @@ static HRESULT WINAPI token_enum_Item( ISpObjectTokenEnumBuilder *iface, return HRESULT_FROM_WIN32(ret); } + hr = create_data_key_with_hkey( key, &data_key ); + if (FAILED(hr)) + { + free( subkey ); + RegCloseKey( key ); + return hr; + } + hr = token_create( NULL, &IID_ISpObjectToken, (void**)&subtoken ); if (FAILED(hr)) { free( subkey ); + ISpRegDataKey_Release( data_key ); return hr; } object = impl_from_ISpObjectToken( subtoken ); - object->token_key = key; + object->data_key = data_key; object->token_id = subkey; *token = subtoken; @@ -1014,7 +1046,7 @@ static ULONG WINAPI token_Release( ISpObjectToken *iface ) if (!ref) { - if (This->token_key) RegCloseKey( This->token_key ); + if (This->data_key) ISpRegDataKey_Release( This->data_key ); free( This->token_id ); free( This ); } @@ -1070,36 +1102,10 @@ static HRESULT WINAPI token_OpenKey( ISpObjectToken *iface, LPCWSTR name, ISpDataKey **sub_key ) { struct object_token *This = impl_from_ISpObjectToken( iface ); - ISpRegDataKey *spregkey; - HRESULT hr; - HKEY key; - LONG ret; TRACE( "%p, %s, %p\n", This, debugstr_w(name), sub_key ); - ret = RegOpenKeyExW (This->token_key, name, 0, KEY_ALL_ACCESS, &key); - if (ret != ERROR_SUCCESS) - return SPERR_NOT_FOUND; - - hr = data_key_create(NULL, &IID_ISpRegDataKey, (void**)&spregkey); - if (FAILED(hr)) - { - RegCloseKey(key); - return hr; - } - - hr = ISpRegDataKey_SetKey(spregkey, key, FALSE); - if (FAILED(hr)) - { - RegCloseKey(key); - ISpRegDataKey_Release(spregkey); - return hr; - } - - hr = ISpRegDataKey_QueryInterface(spregkey, &IID_ISpDataKey, (void**)sub_key); - ISpRegDataKey_Release(spregkey); - - return hr; + return ISpRegDataKey_OpenKey( This->data_key, name, sub_key ); } static HRESULT WINAPI token_CreateKey( ISpObjectToken *iface, @@ -1150,7 +1156,7 @@ static HRESULT WINAPI token_SetId( ISpObjectToken *iface, FIXME( "(%p)->(%s %s %d): semi-stub\n", This, debugstr_w( category_id ), debugstr_w(token_id), create ); - if (This->token_key) return SPERR_ALREADY_INITIALIZED; + if (This->data_key) return SPERR_ALREADY_INITIALIZED; if (!token_id) return E_POINTER; @@ -1163,7 +1169,13 @@ static HRESULT WINAPI token_SetId( ISpObjectToken *iface, res = RegOpenKeyExW( root, subkey, 0, KEY_ALL_ACCESS, &key ); if (res) return SPERR_NOT_FOUND; - This->token_key = key; + hr = create_data_key_with_hkey( key, &This->data_key ); + if (FAILED(hr)) + { + RegCloseKey( key ); + return hr; + } + This->token_id = wcsdup(token_id); return S_OK; @@ -1176,7 +1188,7 @@ static HRESULT WINAPI token_GetId( ISpObjectToken *iface, TRACE( "%p, %p\n", This, token_id); - if (!This->token_key) + if (!This->data_key) return SPERR_UNINITIALIZED; if (!token_id) @@ -1309,7 +1321,7 @@ HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) This->ISpObjectToken_iface.lpVtbl = &token_vtbl; This->ref = 1; - This->token_key = NULL; + This->data_key = NULL; This->token_id = NULL; hr = ISpObjectToken_QueryInterface( &This->ISpObjectToken_iface, iid, obj ); From e22accbd801ca0a5ac8791119adf8435fc5bdc6a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:25:03 -0400 Subject: [PATCH 624/758] sapi: Implement ISpObjectToken::Set/GetStringValue. (cherry picked from commit 26d60533d206a633242f79749f0d54ae2665d134) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/token.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 0465819de56..6f8d677c9d0 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1073,15 +1073,21 @@ static HRESULT WINAPI token_GetData( ISpObjectToken *iface, static HRESULT WINAPI token_SetStringValue( ISpObjectToken *iface, LPCWSTR name, LPCWSTR value ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct object_token *This = impl_from_ISpObjectToken( iface ); + + TRACE( "%p, %s, %s\n", This, debugstr_w(name), debugstr_w(value) ); + + return ISpRegDataKey_SetStringValue( This->data_key, name, value ); } static HRESULT WINAPI token_GetStringValue( ISpObjectToken *iface, LPCWSTR name, LPWSTR *value ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct object_token *This = impl_from_ISpObjectToken( iface ); + + TRACE( "%p, %s, %p\n", This, debugstr_w(name), value ); + + return ISpRegDataKey_GetStringValue( This->data_key, name, value ); } static HRESULT WINAPI token_SetDWORD( ISpObjectToken *iface, From f1c3f34344de26767709d50024da6fa77a84abdc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:32:24 -0400 Subject: [PATCH 625/758] sapi: Implement ISpObjectToken::CreateInstance. (cherry picked from commit bfe018095709fa0f99b40b82e1192d0c89a4cd75) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 149 ++++++++++++++++++++++++++++++++++++++++ dlls/sapi/token.c | 35 +++++++++- 2 files changed, 182 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 2602b043980..3629a86b126 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -268,13 +268,117 @@ static void tests_token_voices(void) IEnumSpObjectTokens_Release(tokens); } + +#define TESTCLASS_CLSID L"{67DD26B6-50BA-3297-253E-619346F177F8}" +static const GUID CLSID_TestClass = {0x67DD26B6,0x50BA,0x3297,{0x25,0x3E,0x61,0x93,0x46,0xF1,0x77,0xF8}}; + +static ISpObjectToken *test_class_token; + +static HRESULT WINAPI test_class_QueryInterface(ISpObjectWithToken *iface, REFIID riid, void **ppv) +{ + if (IsEqualGUID( riid, &IID_IUnknown ) || IsEqualGUID( riid, &IID_ISpObjectWithToken )) + { + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_class_AddRef(ISpObjectWithToken *iface) +{ + return 2; +} + +static ULONG WINAPI test_class_Release(ISpObjectWithToken *iface) +{ + return 1; +} + +static HRESULT WINAPI test_class_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token) +{ + ok( token != NULL, "token == NULL\n" ); + test_class_token = token; + ISpObjectToken_AddRef(test_class_token); + return S_OK; +} + +static HRESULT WINAPI test_class_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token) +{ + ok( 0, "unexpected call\n" ); + return E_NOTIMPL; +} + +static const ISpObjectWithTokenVtbl test_class_vtbl = { + test_class_QueryInterface, + test_class_AddRef, + test_class_Release, + test_class_SetObjectToken, + test_class_GetObjectToken +}; + +static ISpObjectWithToken test_class = { &test_class_vtbl }; + +static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) +{ + if (IsEqualGUID( &IID_IUnknown, riid ) || IsEqualGUID( &IID_IClassFactory, riid )) + { + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, + IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + ok( pUnkOuter == NULL, "pUnkOuter != NULL\n" ); + ok( IsEqualGUID(riid, &IID_IUnknown), "riid = %s\n", wine_dbgstr_guid(riid) ); + + *ppv = &test_class; + return S_OK; +} + +static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock) +{ + ok( 0, "unexpected call\n" ); + return E_NOTIMPL; +} + +static const IClassFactoryVtbl ClassFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + ClassFactory_CreateInstance, + ClassFactory_LockServer +}; + +static IClassFactory test_class_cf = { &ClassFactoryVtbl }; + static void test_object_token(void) { + static const WCHAR test_token_id[] = L"HKEY_CURRENT_USER\\Software\\Winetest\\sapi\\TestToken"; + ISpObjectToken *token; ISpDataKey *sub_key; HRESULT hr; LPWSTR tempW, token_id; ISpObjectTokenCategory *cat; + DWORD regid; + IUnknown *obj; hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectToken, (void **)&token ); @@ -397,6 +501,51 @@ static void test_object_token(void) ISpObjectTokenCategory_Release( cat ); } + hr = CoRegisterClassObject( &CLSID_TestClass, (IUnknown *)&test_class_cf, + CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, ®id ); + ok( hr == S_OK, "got %08lx\n", hr ); + + ISpObjectToken_Release( token ); + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&token ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectToken_SetId( token, NULL, test_token_id, TRUE ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectToken_SetStringValue( token, L"CLSID", TESTCLASS_CLSID ); + ok( hr == S_OK, "got %08lx\n", hr ); + + tempW = NULL; + hr = ISpObjectToken_GetStringValue( token, L"CLSID", &tempW ); + + ok( hr == S_OK, "got %08lx\n", hr ); + if ( tempW ) { + ok( !wcsncmp( tempW, TESTCLASS_CLSID, wcslen(TESTCLASS_CLSID) ), + "got %s (expected %s)\n", wine_dbgstr_w(tempW), wine_dbgstr_w(TESTCLASS_CLSID) ); + CoTaskMemFree( tempW ); + } + + test_class_token = NULL; + hr = ISpObjectToken_CreateInstance( token, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&obj ); + if ( hr == E_ACCESSDENIED ) { + win_skip( "ISpObjectToken_CreateInstance returned E_ACCESSDENIED\n" ); + return; + } + ok( hr == S_OK, "got %08lx\n", hr ); + ok( test_class_token != NULL, "test_class_token not set\n" ); + + tempW = NULL; + hr = ISpObjectToken_GetId( test_class_token, &tempW ); + ok( tempW != NULL, "got %p\n", tempW ); + if (tempW) { + ok( !wcsncmp(tempW, test_token_id, wcslen(test_token_id)), + "got %s (expected %s)\n", wine_dbgstr_w(tempW), wine_dbgstr_w(test_token_id) ); + CoTaskMemFree( tempW ); + } + + ISpObjectToken_Release( test_class_token ); + IUnknown_Release( obj ); ISpObjectToken_Release( token ); } diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 6f8d677c9d0..6cc7ccd6d73 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1227,8 +1227,39 @@ static HRESULT WINAPI token_CreateInstance( ISpObjectToken *iface, REFIID riid, void **object ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + WCHAR *clsid_str; + CLSID clsid; + IUnknown *unk; + ISpObjectWithToken *obj_token_iface; + HRESULT hr; + + TRACE( "%p, %p, %#lx, %s, %p\n", iface, outer, class_context, debugstr_guid( riid ), object ); + + if (FAILED(hr = ISpObjectToken_GetStringValue( iface, L"CLSID", &clsid_str ))) + return hr; + + hr = CLSIDFromString( clsid_str, &clsid ); + CoTaskMemFree( clsid_str ); + if (FAILED(hr)) + return hr; + + if (FAILED(hr = CoCreateInstance( &clsid, outer, class_context, &IID_IUnknown, (void **)&unk ))) + return hr; + + /* Call ISpObjectWithToken::SetObjectToken if the interface is available. */ + if (SUCCEEDED(IUnknown_QueryInterface( unk, &IID_ISpObjectWithToken, (void **)&obj_token_iface ))) + { + hr = ISpObjectWithToken_SetObjectToken( obj_token_iface, iface ); + ISpObjectWithToken_Release( obj_token_iface ); + if (FAILED(hr)) + goto done; + } + + hr = IUnknown_QueryInterface( unk, riid, object ); + +done: + IUnknown_Release( unk ); + return hr; } static HRESULT WINAPI token_GetStorageFileName( ISpObjectToken *iface, From 75003698d80455ebe3e4cd2db01cfe3ae581bbcc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 25 May 2023 23:35:28 -0400 Subject: [PATCH 626/758] sapi: Implement ISpObjectToken::CreateKey. (cherry picked from commit e071a70ba870cee586d28beca8f8965f63378eef) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 4 ++++ dlls/sapi/token.c | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 3629a86b126..287c74a3e58 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -513,6 +513,10 @@ static void test_object_token(void) hr = ISpObjectToken_SetId( token, NULL, test_token_id, TRUE ); ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectToken_CreateKey( token, L"Attributes", &sub_key ); + ok( hr == S_OK, "got %08lx\n", hr ); + ISpDataKey_Release( sub_key ); + hr = ISpObjectToken_SetStringValue( token, L"CLSID", TESTCLASS_CLSID ); ok( hr == S_OK, "got %08lx\n", hr ); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index 6cc7ccd6d73..dbbc14b6e56 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1117,8 +1117,11 @@ static HRESULT WINAPI token_OpenKey( ISpObjectToken *iface, static HRESULT WINAPI token_CreateKey( ISpObjectToken *iface, LPCWSTR name, ISpDataKey **sub_key ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct object_token *This = impl_from_ISpObjectToken( iface ); + + TRACE( "%p, %s, %p\n", iface, debugstr_w(name), sub_key ); + + return ISpRegDataKey_CreateKey( This->data_key, name, sub_key ); } static HRESULT WINAPI token_DeleteKey( ISpObjectToken *iface, From e35daff00a9245a105330613689a2fbdc68f8879 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 00:09:45 -0400 Subject: [PATCH 627/758] sapi: Partially reimplement ISpObjectTokenEnumBuilder storing a token array. This is needed in order to implement filtering and sorting in ISpObjectTokenCategory::EnumTokens. (cherry picked from commit f286ee9bfa9dc0b4fb7f66ad0e406c6c729ce1c8) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 112 ++++++++++++++++- dlls/sapi/token.c | 270 +++++++++++++++++++++++----------------- 2 files changed, 260 insertions(+), 122 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 287c74a3e58..128210fca0b 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -109,6 +109,63 @@ static void test_data_key(void) ISpRegDataKey_Release( data_key ); } +static void setup_test_voice_tokens(void) +{ + HKEY key; + ISpRegDataKey *data_key; + ISpDataKey *attrs_key; + LSTATUS ret; + HRESULT hr; + + ret = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\\Winetest\\sapi\\TestVoices\\Tokens", 0, NULL, 0, + KEY_ALL_ACCESS, NULL, &key, NULL ); + ok( ret == ERROR_SUCCESS, "got %ld\n", ret ); + + hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpRegDataKey, (void **)&data_key ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpRegDataKey_SetKey( data_key, key, FALSE ); + ok( hr == S_OK, "got %08lx\n", hr ); + + ISpRegDataKey_CreateKey( data_key, L"Voice1\\Attributes", &attrs_key ); + ISpDataKey_SetStringValue( attrs_key, L"Language", L"409" ); + ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" ); + ISpDataKey_SetStringValue( attrs_key, L"Age", L"Child" ); + ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor2" ); + ISpDataKey_Release( attrs_key ); + + ISpRegDataKey_CreateKey( data_key, L"Voice2\\Attributes", &attrs_key ); + ISpDataKey_SetStringValue( attrs_key, L"Language", L"406;407;408;409;40a" ); + ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" ); + ISpDataKey_SetStringValue( attrs_key, L"Age", L"Adult" ); + ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor1" ); + ISpDataKey_Release( attrs_key ); + + ISpRegDataKey_CreateKey( data_key, L"Voice3\\Attributes", &attrs_key ); + ISpDataKey_SetStringValue( attrs_key, L"Language", L"409;411" ); + ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" ); + ISpDataKey_SetStringValue( attrs_key, L"Age", L"Child" ); + ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor1" ); + ISpDataKey_Release( attrs_key ); + + ISpRegDataKey_CreateKey( data_key, L"Voice4\\Attributes", &attrs_key ); + ISpDataKey_SetStringValue( attrs_key, L"Language", L"411" ); + ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Male" ); + ISpDataKey_SetStringValue( attrs_key, L"Age", L"Adult" ); + ISpDataKey_Release( attrs_key ); + + ISpRegDataKey_CreateKey( data_key, L"Voice5\\Attributes", &attrs_key ); + ISpDataKey_SetStringValue( attrs_key, L"Language", L"411" ); + ISpDataKey_SetStringValue( attrs_key, L"Gender", L"Female" ); + ISpDataKey_SetStringValue( attrs_key, L"Age", L"Adult" ); + ISpDataKey_SetStringValue( attrs_key, L"Vendor", L"Vendor2" ); + ISpDataKey_Release( attrs_key ); + + ISpRegDataKey_Release( data_key ); +} + +static const WCHAR test_cat[] = L"HKEY_CURRENT_USER\\Software\\Winetest\\sapi\\TestVoices"; + static void test_token_category(void) { ISpObjectTokenCategory *cat; @@ -126,17 +183,19 @@ static void test_token_category(void) hr = ISpObjectTokenCategory_SetId( cat, L"bogus", FALSE ); ok( hr == SPERR_INVALID_REGISTRY_KEY, "got %08lx\n", hr ); - hr = ISpObjectTokenCategory_SetId( cat, SPCAT_VOICES, FALSE ); + hr = ISpObjectTokenCategory_SetId( cat, test_cat, FALSE ); ok( hr == S_OK, "got %08lx\n", hr ); - hr = ISpObjectTokenCategory_SetId( cat, SPCAT_VOICES, FALSE ); + hr = ISpObjectTokenCategory_SetId( cat, test_cat, FALSE ); ok( hr == SPERR_ALREADY_INITIALIZED, "got %08lx\n", hr ); hr = ISpObjectTokenCategory_EnumTokens( cat, NULL, NULL, &enum_tokens ); ok( hr == S_OK, "got %08lx\n", hr ); + count = 0xdeadbeef; hr = IEnumSpObjectTokens_GetCount( enum_tokens, &count ); ok( hr == S_OK, "got %08lx\n", hr ); + ok( count == 5, "got %lu\n", count ); IEnumSpObjectTokens_Release( enum_tokens ); ISpObjectTokenCategory_Release( cat ); @@ -146,8 +205,11 @@ static void test_token_enum(void) { ISpObjectTokenEnumBuilder *token_enum; HRESULT hr; - ISpObjectToken *token; + ISpObjectToken *tokens[3]; + ISpObjectToken *out_tokens[3]; + WCHAR token_id[MAX_PATH]; ULONG count; + int i; hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); @@ -156,7 +218,7 @@ static void test_token_enum(void) hr = ISpObjectTokenEnumBuilder_GetCount( token_enum, &count ); ok( hr == SPERR_UNINITIALIZED, "got %08lx\n", hr ); - hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &token, &count ); + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, tokens, &count ); ok( hr == SPERR_UNINITIALIZED, "got %08lx\n", hr ); hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, NULL, NULL ); @@ -168,11 +230,50 @@ static void test_token_enum(void) ok( count == 0, "got %lu\n", count ); count = 0xdeadbeef; - hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &token, &count ); + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &out_tokens[0], &count ); ok( hr == S_FALSE, "got %08lx\n", hr ); ok( count == 0, "got %lu\n", count ); + for ( i = 0; i < 3; i++ ) { + hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&tokens[i] ); + ok( hr == S_OK, "got %08lx\n", hr ); + + swprintf( token_id, MAX_PATH, L"%ls\\Tokens\\Voice%d", test_cat, i + 1 ); + hr = ISpObjectToken_SetId( tokens[i], NULL, token_id, FALSE ); + ok( hr == S_OK, "got %08lx\n", hr ); + } + + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 3, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + out_tokens[0] = (ISpObjectToken *)0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 1, &out_tokens[0], NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( out_tokens[0] == tokens[0], "got %p\n", out_tokens[0] ); + + out_tokens[0] = (ISpObjectToken *)0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Item( token_enum, 0, &out_tokens[0] ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( out_tokens[0] == tokens[0], "got %p\n", out_tokens[0] ); + + hr = ISpObjectTokenEnumBuilder_Item( token_enum, 3, &out_tokens[0] ); + ok( hr == SPERR_NO_MORE_ITEMS, "got %08lx\n", hr ); + + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 3, &out_tokens[1], NULL ); + ok( hr == E_POINTER, "got %08lx\n", hr ); + + count = 0xdeadbeef; + out_tokens[1] = out_tokens[2] = (ISpObjectToken *)0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 3, &out_tokens[1], &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 2, "got %lu\n", count ); + ok( out_tokens[1] == tokens[1], "got %p\n", out_tokens[1] ); + ok( out_tokens[2] == tokens[2], "got %p\n", out_tokens[2] ); + ISpObjectTokenEnumBuilder_Release( token_enum ); + + for ( i = 0; i < 3; i++ ) ISpObjectToken_Release( tokens[i] ); } static void test_default_token_id(void) @@ -558,6 +659,7 @@ START_TEST(token) CoInitialize( NULL ); RegDeleteTreeA( HKEY_CURRENT_USER, "Software\\Winetest\\sapi" ); test_data_key(); + setup_test_voice_tokens(); test_token_category(); test_token_enum(); test_default_token_id(); diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index dbbc14b6e56..fedf70e69d5 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -19,6 +19,7 @@ */ #include +#include #define COBJMACROS @@ -328,6 +329,7 @@ struct token_category LONG ref; ISpRegDataKey *data_key; + WCHAR *id; }; static struct token_category *impl_from_ISpObjectTokenCategory( ISpObjectTokenCategory *iface ) @@ -375,6 +377,7 @@ static ULONG WINAPI token_category_Release( ISpObjectTokenCategory *iface ) if (!ref) { if (This->data_key) ISpRegDataKey_Release( This->data_key ); + free( This->id ); free( This ); } return ref; @@ -541,6 +544,8 @@ static HRESULT WINAPI token_category_SetId( ISpObjectTokenCategory *iface, return hr; } + This->id = wcsdup( id ); + return hr; } @@ -559,6 +564,12 @@ static HRESULT WINAPI token_category_GetDataKey( ISpObjectTokenCategory *iface, return E_NOTIMPL; } +struct token_with_score +{ + ISpObjectToken *token; + uint64_t score; +}; + struct token_enum { ISpObjectTokenEnumBuilder ISpObjectTokenEnumBuilder_iface; @@ -566,8 +577,8 @@ struct token_enum BOOL init; WCHAR *req, *opt; - ULONG count; - HKEY key; + struct token_with_score *tokens; + ULONG capacity, count; DWORD index; }; @@ -582,8 +593,12 @@ static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface, { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); ISpObjectTokenEnumBuilder *builder; - struct token_enum *tokenenum; struct data_key *this_data_key; + HKEY tokens_key; + DWORD count, max_subkey_size, root_len, token_id_size; + DWORD size, i; + WCHAR *token_id = NULL; + ISpObjectToken *token = NULL; HRESULT hr; TRACE( "(%p)->(%s %s %p)\n", This, debugstr_w( req ), debugstr_w( opt ), enum_tokens ); @@ -599,12 +614,43 @@ static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface, this_data_key = impl_from_ISpRegDataKey( This->data_key ); - tokenenum = impl_from_ISpObjectTokenEnumBuilder( builder ); - - if (!RegOpenKeyExW( this_data_key->key, L"Tokens", 0, KEY_ALL_ACCESS, &tokenenum->key )) + if (!RegOpenKeyExW( this_data_key->key, L"Tokens", 0, KEY_ALL_ACCESS, &tokens_key )) { - RegQueryInfoKeyW(tokenenum->key, NULL, NULL, NULL, &tokenenum->count, NULL, NULL, - NULL, NULL, NULL, NULL, NULL); + RegQueryInfoKeyW( tokens_key, NULL, NULL, NULL, &count, &max_subkey_size, NULL, + NULL, NULL, NULL, NULL, NULL ); + max_subkey_size++; + + root_len = wcslen( This->id ); + token_id_size = root_len + sizeof("\\Tokens\\") + max_subkey_size; + token_id = malloc( token_id_size * sizeof(WCHAR) ); + if (!token_id) + { + hr = E_OUTOFMEMORY; + goto fail; + } + root_len = swprintf( token_id, token_id_size, L"%ls%lsTokens\\", + This->id, This->id[root_len - 1] == L'\\' ? L"" : L"\\" ); + + for ( i = 0; i < count; i++ ) + { + size = max_subkey_size; + hr = HRESULT_FROM_WIN32(RegEnumKeyExW( tokens_key, i, token_id + root_len, &size, NULL, NULL, NULL, NULL )); + if (FAILED(hr)) goto fail; + + hr = token_create( NULL, &IID_ISpObjectToken, (void **)&token ); + if (FAILED(hr)) goto fail; + + hr = ISpObjectToken_SetId( token, NULL, token_id, FALSE ); + if (FAILED(hr)) goto fail; + + hr = ISpObjectTokenEnumBuilder_AddTokens( builder, 1, &token ); + if (FAILED(hr)) goto fail; + ISpObjectToken_Release( token ); + token = NULL; + } + + hr = ISpObjectTokenEnumBuilder_Sort( builder, NULL ); + if (FAILED(hr)) goto fail; } hr = ISpObjectTokenEnumBuilder_QueryInterface( builder, &IID_IEnumSpObjectTokens, @@ -612,6 +658,8 @@ static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface, fail: ISpObjectTokenEnumBuilder_Release( builder ); + if ( token ) ISpObjectToken_Release( token ); + free( token_id ); return hr; } @@ -693,6 +741,7 @@ HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj ) This->ISpObjectTokenCategory_iface.lpVtbl = &token_category_vtbl; This->ref = 1; This->data_key = NULL; + This->id = NULL; hr = ISpObjectTokenCategory_QueryInterface( &This->ISpObjectTokenCategory_iface, iid, obj ); @@ -739,10 +788,16 @@ static ULONG WINAPI token_enum_Release( ISpObjectTokenEnumBuilder *iface ) if (!ref) { - if (This->key) - RegCloseKey(This->key); free( This->req ); free( This->opt ); + if (This->tokens) + { + ULONG i; + for ( i = 0; i < This->count; i++ ) + if ( This->tokens[i].token ) + ISpObjectToken_Release( This->tokens[i].token ); + free( This->tokens ); + } free( This ); } @@ -754,64 +809,23 @@ static HRESULT WINAPI token_enum_Next( ISpObjectTokenEnumBuilder *iface, ULONG *fetched ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); - struct object_token *object; - HRESULT hr; - DWORD retCode; - WCHAR *subkey_name; - HKEY sub_key; - DWORD size; - ISpRegDataKey *data_key; + ULONG i; TRACE( "(%p)->(%lu %p %p)\n", This, num, tokens, fetched ); if (!This->init) return SPERR_UNINITIALIZED; - if (fetched) *fetched = 0; - - *tokens = NULL; - - RegQueryInfoKeyW( This->key, NULL, NULL, NULL, NULL, &size, NULL, NULL, NULL, NULL, NULL, NULL ); - size = (size+1) * sizeof(WCHAR); - subkey_name = malloc( size ); - if (!subkey_name) - return E_OUTOFMEMORY; + if (!fetched && num != 1) return E_POINTER; + if (!tokens) return E_POINTER; - retCode = RegEnumKeyExW( This->key, This->index, subkey_name, &size, NULL, NULL, NULL, NULL ); - if (retCode != ERROR_SUCCESS) + for ( i = 0; i < num && This->index < This->count; i++, This->index++ ) { - free( subkey_name ); - return S_FALSE; + ISpObjectToken_AddRef( This->tokens[This->index].token ); + tokens[i] = This->tokens[This->index].token; } - This->index++; - - if (RegOpenKeyExW( This->key, subkey_name, 0, KEY_READ, &sub_key ) != ERROR_SUCCESS) - { - free( subkey_name ); - return E_FAIL; - } + if (fetched) *fetched = i; - hr = create_data_key_with_hkey( sub_key, &data_key ); - if (FAILED(hr)) - { - free( subkey_name ); - RegCloseKey( sub_key ); - return hr; - } - - hr = token_create( NULL, &IID_ISpObjectToken, (void**)tokens ); - if (FAILED(hr)) - { - free( subkey_name ); - ISpRegDataKey_Release( data_key ); - return hr; - } - - object = impl_from_ISpObjectToken( *tokens ); - object->data_key = data_key; - object->token_id = subkey_name; - - if (fetched) *fetched = 1; - return hr; + return i == num ? S_OK : S_FALSE; } static HRESULT WINAPI token_enum_Skip( ISpObjectTokenEnumBuilder *iface, @@ -838,63 +852,18 @@ static HRESULT WINAPI token_enum_Item( ISpObjectTokenEnumBuilder *iface, ULONG index, ISpObjectToken **token ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); - struct object_token *object; - ISpObjectToken *subtoken; - HRESULT hr; - WCHAR *subkey; - DWORD size; - LONG ret; - HKEY key; - ISpRegDataKey *data_key; - - TRACE( "%p, %lu, %p\n", This, index, token ); - - if (!This->init) - return SPERR_UNINITIALIZED; - - RegQueryInfoKeyW(This->key, NULL, NULL, NULL, NULL, &size, NULL, NULL, NULL, NULL, NULL, NULL); - size = (size+1) * sizeof(WCHAR); - subkey = malloc( size ); - if (!subkey) - return E_OUTOFMEMORY; - - ret = RegEnumKeyExW(This->key, index, subkey, &size, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) - { - free( subkey ); - return HRESULT_FROM_WIN32(ret); - } - - ret = RegOpenKeyExW (This->key, subkey, 0, KEY_READ, &key); - if (ret != ERROR_SUCCESS) - { - free( subkey ); - return HRESULT_FROM_WIN32(ret); - } - hr = create_data_key_with_hkey( key, &data_key ); - if (FAILED(hr)) - { - free( subkey ); - RegCloseKey( key ); - return hr; - } + TRACE( "(%p)->(%lu %p)\n", This, index, token ); - hr = token_create( NULL, &IID_ISpObjectToken, (void**)&subtoken ); - if (FAILED(hr)) - { - free( subkey ); - ISpRegDataKey_Release( data_key ); - return hr; - } + if (!This->init) return SPERR_UNINITIALIZED; - object = impl_from_ISpObjectToken( subtoken ); - object->data_key = data_key; - object->token_id = subkey; + if (!token) return E_POINTER; + if (index >= This->count) return SPERR_NO_MORE_ITEMS; - *token = subtoken; + ISpObjectToken_AddRef( This->tokens[index].token ); + *token = This->tokens[index].token; - return hr; + return S_OK; } static HRESULT WINAPI token_enum_GetCount( ISpObjectTokenEnumBuilder *iface, @@ -939,11 +908,60 @@ static HRESULT WINAPI token_enum_SetAttribs( ISpObjectTokenEnumBuilder *iface, return E_OUTOFMEMORY; } +static BOOL grow_tokens_array( struct token_enum *This ) +{ + struct token_with_score *new_tokens; + ULONG new_cap; + + if (This->count < This->capacity) return TRUE; + + if (This->capacity > 0) + { + new_cap = This->capacity * 2; + new_tokens = realloc( This->tokens, new_cap * sizeof(*new_tokens) ); + } + else + { + new_cap = 1; + new_tokens = malloc( sizeof(*new_tokens) ); + } + + if (!new_tokens) return FALSE; + + This->tokens = new_tokens; + This->capacity = new_cap; + return TRUE; +} + static HRESULT WINAPI token_enum_AddTokens( ISpObjectTokenEnumBuilder *iface, ULONG num, ISpObjectToken **tokens ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); + ULONG i; + + TRACE( "(%p)->(%lu %p)\n", iface, num, tokens ); + + if (!This->init) return SPERR_UNINITIALIZED; + if (!tokens) return E_POINTER; + + if (This->req || This->opt) + { + FIXME( "filtering and sorting of tokens is not implemented\n" ); + return E_NOTIMPL; + } + + for ( i = 0; i < num; i++ ) + { + if (!tokens[i]) return E_POINTER; + + if (!grow_tokens_array( This )) return E_OUTOFMEMORY; + ISpObjectToken_AddRef( tokens[i] ); + This->tokens[This->count].token = tokens[i]; + This->tokens[This->count].score = 0; + This->count++; + } + + return S_OK; } static HRESULT WINAPI token_enum_AddTokensFromDataKey( ISpObjectTokenEnumBuilder *iface, @@ -964,8 +982,26 @@ static HRESULT WINAPI token_enum_AddTokensFromTokenEnum( ISpObjectTokenEnumBuild static HRESULT WINAPI token_enum_Sort( ISpObjectTokenEnumBuilder *iface, LPCWSTR first ) { - FIXME( "stub\n" ); - return E_NOTIMPL; + struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); + + FIXME( "(%p)->(%s): semi-stub\n", iface, debugstr_w(first) ); + + if (!This->init) return SPERR_UNINITIALIZED; + if (!This->tokens) return S_OK; + + if (first) + { + FIXME( "first != NULL is not implemented.\n" ); + return E_NOTIMPL; + } + + if (This->opt) + { + FIXME( "sorting with optional attributes is not implemented.\n" ); + return E_NOTIMPL; + } + + return S_OK; } const struct ISpObjectTokenEnumBuilderVtbl token_enum_vtbl = @@ -997,8 +1033,8 @@ HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) This->req = NULL; This->opt = NULL; This->init = FALSE; - This->count = 0; - This->key = NULL; + This->tokens = NULL; + This->capacity = This->count = 0; This->index = 0; hr = ISpObjectTokenEnumBuilder_QueryInterface( &This->ISpObjectTokenEnumBuilder_iface, iid, obj ); From f7eeaeca1c7e985ab82dafca782e933075ec7b11 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 00:35:48 -0400 Subject: [PATCH 628/758] sapi: Implement token filtering and sorting in ISpObjectTokenEnumBuilder. (cherry picked from commit db2640d06b49b7d28fe556d582de8b5a4ae6ccea) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 138 ++++++++++++++++++++++++++++++++++++++-- dlls/sapi/token.c | 131 ++++++++++++++++++++++++++++++++++---- 2 files changed, 253 insertions(+), 16 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 128210fca0b..5a3bc0e340e 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -172,6 +172,10 @@ static void test_token_category(void) IEnumSpObjectTokens *enum_tokens; HRESULT hr; ULONG count; + int i; + ISpObjectToken *token; + WCHAR *token_id; + WCHAR tmp[MAX_PATH]; hr = CoCreateInstance( &CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectTokenCategory, (void **)&cat ); @@ -198,6 +202,43 @@ static void test_token_category(void) ok( count == 5, "got %lu\n", count ); IEnumSpObjectTokens_Release( enum_tokens ); + + hr = ISpObjectTokenCategory_EnumTokens( cat, L"Language=409", NULL, &enum_tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = IEnumSpObjectTokens_GetCount( enum_tokens, &count ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( count == 3, "got %lu\n", count ); + + IEnumSpObjectTokens_Release( enum_tokens ); + + hr = ISpObjectTokenCategory_EnumTokens( cat, L"Language=409", L"Vendor=Vendor1;Age=Child;Gender=Female", + &enum_tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = IEnumSpObjectTokens_GetCount( enum_tokens, &count ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( count == 3, "got %lu\n", count ); + + for ( i = 0; i < 3; i++ ) { + token = NULL; + hr = IEnumSpObjectTokens_Item( enum_tokens, i, &token ); + ok( hr == S_OK, "i = %d: got %08lx\n", i, hr ); + + token_id = NULL; + hr = ISpObjectToken_GetId( token, &token_id ); + ok( hr == S_OK, "i = %d: got %08lx\n", i, hr ); + swprintf( tmp, MAX_PATH, L"%ls\\Tokens\\Voice%d", test_cat, 3 - i ); + ok( !wcscmp( token_id, tmp ), "i = %d: got %s\n", i, wine_dbgstr_w(token_id) ); + + CoTaskMemFree( token_id ); + ISpObjectToken_Release( token ); + } + + IEnumSpObjectTokens_Release( enum_tokens ); + ISpObjectTokenCategory_Release( cat ); } @@ -205,8 +246,8 @@ static void test_token_enum(void) { ISpObjectTokenEnumBuilder *token_enum; HRESULT hr; - ISpObjectToken *tokens[3]; - ISpObjectToken *out_tokens[3]; + ISpObjectToken *tokens[5]; + ISpObjectToken *out_tokens[5]; WCHAR token_id[MAX_PATH]; ULONG count; int i; @@ -234,7 +275,7 @@ static void test_token_enum(void) ok( hr == S_FALSE, "got %08lx\n", hr ); ok( count == 0, "got %lu\n", count ); - for ( i = 0; i < 3; i++ ) { + for ( i = 0; i < 5; i++ ) { hr = CoCreateInstance( &CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, &IID_ISpObjectToken, (void **)&tokens[i] ); ok( hr == S_OK, "got %08lx\n", hr ); @@ -273,7 +314,96 @@ static void test_token_enum(void) ISpObjectTokenEnumBuilder_Release( token_enum ); - for ( i = 0; i < 3; i++ ) ISpObjectToken_Release( tokens[i] ); + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + + /* Vendor attribute must exist */ + hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, L"Vendor", NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 5, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 5, &out_tokens[0], &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 4, "got %lu\n", count ); + + ISpObjectTokenEnumBuilder_Release( token_enum ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + + /* Vendor attribute must contain Vendor1 */ + hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, L"Vendor=Vendor1", NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 5, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 5, &out_tokens[0], &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 2, "got %lu\n", count ); + + ISpObjectTokenEnumBuilder_Release( token_enum ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + + /* Vendor attribute must not contain Vendor1 */ + hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, L"Vendor!=Vendor1", NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 5, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 5, &out_tokens[0], &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 3, "got %lu\n", count ); + + ISpObjectTokenEnumBuilder_Release( token_enum ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + + /* Vendor attribute must contain Vendor1 and Language attribute must contain 407 */ + hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, L"Vendor=Vendor1;Language=407", NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 5, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 5, &out_tokens[0], &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 1, "got %lu\n", count ); + + hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenEnumBuilder, (void **)&token_enum ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectTokenEnumBuilder_SetAttribs( token_enum, L"Language=409", + L"Vendor=Vendor1;Age=Child;Gender=Female" ); + ok( hr == S_OK, "got %08lx\n", hr ); + hr = ISpObjectTokenEnumBuilder_AddTokens( token_enum, 5, tokens ); + ok( hr == S_OK, "got %08lx\n", hr ); + + hr = ISpObjectTokenEnumBuilder_Sort( token_enum, NULL ); + ok( hr == S_OK, "got %08lx\n", hr ); + + count = 0xdeadbeef; + hr = ISpObjectTokenEnumBuilder_Next( token_enum, 5, &out_tokens[0], &count ); + ok( hr == S_FALSE, "got %08lx\n", hr ); + ok( count == 3, "got %lu\n", count ); + ok( out_tokens[0] == tokens[2], "got %p\n", out_tokens[0] ); + ok( out_tokens[1] == tokens[1], "got %p\n", out_tokens[1] ); + ok( out_tokens[2] == tokens[0], "got %p\n", out_tokens[2] ); + + ISpObjectTokenEnumBuilder_Release( token_enum ); + + for ( i = 0; i < 5; i++ ) ISpObjectToken_Release( tokens[i] ); } static void test_default_token_id(void) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index fedf70e69d5..af98db02048 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -908,6 +908,104 @@ static HRESULT WINAPI token_enum_SetAttribs( ISpObjectTokenEnumBuilder *iface, return E_OUTOFMEMORY; } +static HRESULT score_attributes( ISpObjectToken *token, const WCHAR *attrs, + BOOL match_all, uint64_t *score ) +{ + ISpDataKey *attrs_key; + WCHAR *attr, *attr_ctx, *buf; + BOOL match[64]; + unsigned int i, j; + HRESULT hr; + + if (!attrs) + { + *score = 1; + return S_OK; + } + *score = 0; + + if (FAILED(hr = ISpObjectToken_OpenKey( token, L"Attributes", &attrs_key ))) + return hr == SPERR_NOT_FOUND ? S_OK : hr; + + memset( match, 0, sizeof(match) ); + + /* attrs is a semicolon-separated list of attribute clauses. + * Each clause consists of an attribute name and an optional operator and value. + * The meaning of a clause depends on the operator given: + * If no operator is given, the attribute must exist. + * If the operator is '=', the attribute must contain the given value. + * If the operator is '!=', the attribute must not exist or contain the given value. + */ + if (!(buf = wcsdup( attrs ))) return E_OUTOFMEMORY; + for ( attr = wcstok_s( buf, L";", &attr_ctx ), i = 0; attr && i < 64; + attr = wcstok_s( NULL, L";", &attr_ctx ), i++ ) + { + WCHAR *p = wcspbrk( attr, L"!=" ); + WCHAR op = p ? *p : L'\0'; + WCHAR *value = NULL, *res; + if ( p ) + { + if ( op == L'=' ) + value = p + 1; + else if ( op == L'!' ) + { + if ( *(p + 1) != L'=' ) + { + WARN( "invalid attr operator '!%lc'.\n", *(p + 1) ); + hr = E_INVALIDARG; + goto done; + } + value = p + 2; + } + *p = L'\0'; + } + + hr = ISpDataKey_GetStringValue( attrs_key, attr, &res ); + if ( p ) *p = op; + if (SUCCEEDED(hr)) + { + if ( !op ) + match[i] = TRUE; + else + { + WCHAR *val, *val_ctx; + + match[i] = FALSE; + for ( val = wcstok_s( res, L";", &val_ctx ); val && !match[i]; + val = wcstok_s( NULL, L";", &val_ctx ) ) + match[i] = !wcscmp( val, value ); + + if (op == L'!') match[i] = !match[i]; + } + CoTaskMemFree( res ); + } + else if (hr == SPERR_NOT_FOUND) + { + hr = S_OK; + if (op == L'!') match[i] = TRUE; + } + else + goto done; + + if ( match_all && !match[i] ) + goto done; + } + + if ( attr ) + hr = E_INVALIDARG; + else + { + /* Attributes in attrs are ordered from highest to lowest priority. */ + for ( j = 0; j < i; j++ ) + if ( match[j] ) + *score |= 1ULL << (i - 1 - j); + } + +done: + free( buf ); + return hr; +} + static BOOL grow_tokens_array( struct token_enum *This ) { struct token_with_score *new_tokens; @@ -938,26 +1036,29 @@ static HRESULT WINAPI token_enum_AddTokens( ISpObjectTokenEnumBuilder *iface, { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); ULONG i; + uint64_t score; + HRESULT hr; TRACE( "(%p)->(%lu %p)\n", iface, num, tokens ); if (!This->init) return SPERR_UNINITIALIZED; if (!tokens) return E_POINTER; - if (This->req || This->opt) - { - FIXME( "filtering and sorting of tokens is not implemented\n" ); - return E_NOTIMPL; - } - for ( i = 0; i < num; i++ ) { if (!tokens[i]) return E_POINTER; + hr = score_attributes( tokens[i], This->req, TRUE, &score ); + if (FAILED(hr)) return hr; + if (!score) continue; + + hr = score_attributes( tokens[i], This->opt, FALSE, &score ); + if (FAILED(hr)) return hr; + if (!grow_tokens_array( This )) return E_OUTOFMEMORY; ISpObjectToken_AddRef( tokens[i] ); This->tokens[This->count].token = tokens[i]; - This->tokens[This->count].score = 0; + This->tokens[This->count].score = score; This->count++; } @@ -979,12 +1080,21 @@ static HRESULT WINAPI token_enum_AddTokensFromTokenEnum( ISpObjectTokenEnumBuild return E_NOTIMPL; } +static int __cdecl token_with_score_cmp( const void *a, const void *b ) +{ + const struct token_with_score *ta = a, *tb = b; + + if (ta->score > tb->score) return -1; + else if (ta->score < tb->score) return 1; + else return 0; +} + static HRESULT WINAPI token_enum_Sort( ISpObjectTokenEnumBuilder *iface, LPCWSTR first ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); - FIXME( "(%p)->(%s): semi-stub\n", iface, debugstr_w(first) ); + TRACE( "(%p)->(%s).\n", iface, debugstr_w(first) ); if (!This->init) return SPERR_UNINITIALIZED; if (!This->tokens) return S_OK; @@ -996,10 +1106,7 @@ static HRESULT WINAPI token_enum_Sort( ISpObjectTokenEnumBuilder *iface, } if (This->opt) - { - FIXME( "sorting with optional attributes is not implemented.\n" ); - return E_NOTIMPL; - } + qsort( This->tokens, This->count, sizeof(*This->tokens), token_with_score_cmp ); return S_OK; } From 512e7aee4dc483e3a15e2760f06b4144dfff5ce7 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 16 May 2023 18:55:31 -0400 Subject: [PATCH 629/758] sapi: Add SpMMAudioOut stub. (cherry picked from commit 33c8d7b8a5b140241ca42da539a5c353f0cd0d09) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/Makefile.in | 1 + dlls/sapi/main.c | 3 + dlls/sapi/mmaudio.c | 633 ++++++++++++++++++++++++++++++++++++ dlls/sapi/sapi_classes.idl | 15 + dlls/sapi/sapi_private.h | 1 + dlls/sapi/tests/Makefile.in | 1 + dlls/sapi/tests/mmaudio.c | 68 ++++ 7 files changed, 722 insertions(+) create mode 100644 dlls/sapi/mmaudio.c create mode 100644 dlls/sapi/tests/mmaudio.c diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index e0f9a8cdd07..ca64edec6dd 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -4,6 +4,7 @@ IMPORTS = uuid ole32 user32 advapi32 C_SRCS = \ automation.c \ main.c \ + mmaudio.c \ resource.c \ stream.c \ token.c \ diff --git a/dlls/sapi/main.c b/dlls/sapi/main.c index d83d2257186..108db7d13d8 100644 --- a/dlls/sapi/main.c +++ b/dlls/sapi/main.c @@ -105,6 +105,7 @@ static const struct IClassFactoryVtbl class_factory_vtbl = static struct class_factory data_key_cf = { { &class_factory_vtbl }, data_key_create }; static struct class_factory file_stream_cf = { { &class_factory_vtbl }, file_stream_create }; +static struct class_factory mmaudio_out_cf = { { &class_factory_vtbl }, mmaudio_out_create }; static struct class_factory resource_mgr_cf = { { &class_factory_vtbl }, resource_manager_create }; static struct class_factory speech_stream_cf = { { &class_factory_vtbl }, speech_stream_create }; static struct class_factory speech_voice_cf = { { &class_factory_vtbl }, speech_voice_create }; @@ -125,6 +126,8 @@ HRESULT WINAPI DllGetClassObject( REFCLSID clsid, REFIID iid, void **obj ) cf = &data_key_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpFileStream )) cf = &file_stream_cf.IClassFactory_iface; + else if (IsEqualCLSID( clsid, &CLSID_SpMMAudioOut )) + cf = &mmaudio_out_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpObjectTokenCategory )) cf = &token_category_cf.IClassFactory_iface; else if (IsEqualCLSID( clsid, &CLSID_SpObjectTokenEnum )) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c new file mode 100644 index 00000000000..684476f5c56 --- /dev/null +++ b/dlls/sapi/mmaudio.c @@ -0,0 +1,633 @@ +/* + * Speech API (SAPI) winmm audio implementation. + * + * Copyright 2023 Shaun Ren 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 + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "sapiddk.h" +#include "sperror.h" + +#include "wine/debug.h" + +#include "sapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sapi); + +enum flow_type { FLOW_IN, FLOW_OUT }; + +struct mmaudio +{ + ISpEventSource ISpEventSource_iface; + ISpEventSink ISpEventSink_iface; + ISpObjectWithToken ISpObjectWithToken_iface; + ISpMMSysAudio ISpMMSysAudio_iface; + LONG ref; + + enum flow_type flow; + ISpObjectToken *token; +}; + +static inline struct mmaudio *impl_from_ISpEventSource(ISpEventSource *iface) +{ + return CONTAINING_RECORD(iface, struct mmaudio, ISpEventSource_iface); +} + +static inline struct mmaudio *impl_from_ISpEventSink(ISpEventSink *iface) +{ + return CONTAINING_RECORD(iface, struct mmaudio, ISpEventSink_iface); +} + +static inline struct mmaudio *impl_from_ISpObjectWithToken(ISpObjectWithToken *iface) +{ + return CONTAINING_RECORD(iface, struct mmaudio, ISpObjectWithToken_iface); +} + +static inline struct mmaudio *impl_from_ISpMMSysAudio(ISpMMSysAudio *iface) +{ + return CONTAINING_RECORD(iface, struct mmaudio, ISpMMSysAudio_iface); +} + +static HRESULT WINAPI event_source_QueryInterface(ISpEventSource *iface, REFIID iid, void **obj) +{ + struct mmaudio *This = impl_from_ISpEventSource(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + return ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); +} + +static ULONG WINAPI event_source_AddRef(ISpEventSource *iface) +{ + struct mmaudio *This = impl_from_ISpEventSource(iface); + + TRACE("(%p).\n", iface); + + return ISpMMSysAudio_AddRef(&This->ISpMMSysAudio_iface); +} + +static ULONG WINAPI event_source_Release(ISpEventSource *iface) +{ + struct mmaudio *This = impl_from_ISpEventSource(iface); + + TRACE("(%p).\n", iface); + + return ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface); +} + +static HRESULT WINAPI event_source_SetNotifySink(ISpEventSource *iface, ISpNotifySink *sink) +{ + FIXME("(%p, %p): stub.\n", iface, sink); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_SetNotifyWindowMessage(ISpEventSource *iface, HWND hwnd, + UINT msg, WPARAM wparam, LPARAM lparam) +{ + FIXME("(%p, %p, %u, %Ix, %Ix): stub.\n", iface, hwnd, msg, wparam, lparam); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_SetNotifyCallbackFunction(ISpEventSource *iface, SPNOTIFYCALLBACK *callback, + WPARAM wparam, LPARAM lparam) +{ + FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface, callback, wparam, lparam); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_SetNotifyCallbackInterface(ISpEventSource *iface, ISpNotifyCallback *callback, + WPARAM wparam, LPARAM lparam) +{ + FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface, callback, wparam, lparam); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_SetNotifyWin32Event(ISpEventSource *iface) +{ + FIXME("(%p): stub.\n", iface); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_WaitForNotifyEvent(ISpEventSource *iface, DWORD milliseconds) +{ + FIXME("(%p, %ld): stub.\n", iface, milliseconds); + + return E_NOTIMPL; +} + +static HANDLE WINAPI event_source_GetNotifyEventHandle(ISpEventSource *iface) +{ + FIXME("(%p): stub.\n", iface); + + return NULL; +} + +static HRESULT WINAPI event_source_SetInterest(ISpEventSource *iface, ULONGLONG event, ULONGLONG queued) +{ + FIXME("(%p, %s, %s): stub.\n", iface, wine_dbgstr_longlong(event), wine_dbgstr_longlong(queued)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_GetEvents(ISpEventSource *iface, ULONG count, SPEVENT *array, ULONG *fetched) +{ + FIXME("(%p, %lu, %p, %p): stub.\n", iface, count, array, fetched); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_source_GetInfo(ISpEventSource *iface, SPEVENTSOURCEINFO *info) +{ + FIXME("(%p, %p): stub.\n", iface, info); + + return E_NOTIMPL; +} + +static const ISpEventSourceVtbl event_source_vtbl = +{ + event_source_QueryInterface, + event_source_AddRef, + event_source_Release, + event_source_SetNotifySink, + event_source_SetNotifyWindowMessage, + event_source_SetNotifyCallbackFunction, + event_source_SetNotifyCallbackInterface, + event_source_SetNotifyWin32Event, + event_source_WaitForNotifyEvent, + event_source_GetNotifyEventHandle, + event_source_SetInterest, + event_source_GetEvents, + event_source_GetInfo +}; + +static HRESULT WINAPI event_sink_QueryInterface(ISpEventSink *iface, REFIID iid, void **obj) +{ + struct mmaudio *This = impl_from_ISpEventSink(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + return ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); +} + +static ULONG WINAPI event_sink_AddRef(ISpEventSink *iface) +{ + struct mmaudio *This = impl_from_ISpEventSink(iface); + + TRACE("(%p).\n", iface); + + return ISpMMSysAudio_AddRef(&This->ISpMMSysAudio_iface); +} + +static ULONG WINAPI event_sink_Release(ISpEventSink *iface) +{ + struct mmaudio *This = impl_from_ISpEventSink(iface); + + TRACE("(%p).\n", iface); + + return ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface); +} + +static HRESULT WINAPI event_sink_AddEvents(ISpEventSink *iface, const SPEVENT *events, ULONG count) +{ + FIXME("(%p, %p, %lu).\n", iface, events, count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI event_sink_GetEventInterest(ISpEventSink *iface, ULONGLONG *interest) +{ + FIXME("(%p, %p).\n", iface, interest); + + return E_NOTIMPL; +} + +static const ISpEventSinkVtbl event_sink_vtbl = +{ + event_sink_QueryInterface, + event_sink_AddRef, + event_sink_Release, + event_sink_AddEvents, + event_sink_GetEventInterest +}; + +static HRESULT WINAPI objwithtoken_QueryInterface(ISpObjectWithToken *iface, REFIID iid, void **obj) +{ + struct mmaudio *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + return ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); +} + +static ULONG WINAPI objwithtoken_AddRef(ISpObjectWithToken *iface) +{ + struct mmaudio *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p).\n", iface); + + return ISpMMSysAudio_AddRef(&This->ISpMMSysAudio_iface); +} + +static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface) +{ + struct mmaudio *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p).\n", iface); + + return ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface); +} + +static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token) +{ + struct mmaudio *This = impl_from_ISpObjectWithToken(iface); + + FIXME("(%p, %p): semi-stub.\n", iface, token); + + if (!token) + return E_INVALIDARG; + if (This->token) + return SPERR_ALREADY_INITIALIZED; + + This->token = token; + return S_OK; +} + +static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token) +{ + struct mmaudio *This = impl_from_ISpObjectWithToken(iface); + + TRACE("(%p, %p).\n", iface, token); + + if (!token) + return E_POINTER; + + *token = This->token; + if (*token) + { + ISpObjectToken_AddRef(*token); + return S_OK; + } + else + return S_FALSE; +} + +static const ISpObjectWithTokenVtbl objwithtoken_vtbl = +{ + objwithtoken_QueryInterface, + objwithtoken_AddRef, + objwithtoken_Release, + objwithtoken_SetObjectToken, + objwithtoken_GetObjectToken +}; + +static HRESULT WINAPI mmsysaudio_QueryInterface(ISpMMSysAudio *iface, REFIID iid, void **obj) +{ + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); + + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_ISequentialStream) || + IsEqualIID(iid, &IID_IStream) || + IsEqualIID(iid, &IID_ISpStreamFormat) || + IsEqualIID(iid, &IID_ISpAudio) || + IsEqualIID(iid, &IID_ISpMMSysAudio)) + *obj = &This->ISpMMSysAudio_iface; + else if (IsEqualIID(iid, &IID_ISpEventSource)) + *obj = &This->ISpEventSource_iface; + else if (IsEqualIID(iid, &IID_ISpEventSink)) + *obj = &This->ISpEventSink_iface; + else if (IsEqualIID(iid, &IID_ISpObjectWithToken)) + *obj = &This->ISpObjectWithToken_iface; + else + { + *obj = NULL; + FIXME("interface %s not implemented.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static ULONG WINAPI mmsysaudio_AddRef(ISpMMSysAudio *iface) +{ + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p): ref=%lu\n", iface, ref); + + return ref; +} + +static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface) +{ + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p): ref=%lu\n", iface, ref); + + if (!ref) + { + if (This->token) ISpObjectToken_Release(This->token); + + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI mmsysaudio_Read(ISpMMSysAudio *iface, void *pv, ULONG cb, ULONG *cb_read) +{ + FIXME("(%p, %p, %lu, %p): stub.\n", iface, pv, cb, cb_read); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_Write(ISpMMSysAudio *iface, const void *pv, ULONG cb, ULONG *cb_written) +{ + FIXME("(%p, %p, %lu, %p): stub.\n", iface, pv, cb, cb_written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_Seek(ISpMMSysAudio *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos) +{ + FIXME("(%p, %s, %lu, %p): stub.\n", iface, wine_dbgstr_longlong(move.QuadPart), origin, new_pos); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetSize(ISpMMSysAudio *iface, ULARGE_INTEGER new_size) +{ + FIXME("(%p, %s): stub.\n", iface, wine_dbgstr_longlong(new_size.QuadPart)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_CopyTo(ISpMMSysAudio *iface, IStream *stream, ULARGE_INTEGER cb, + ULARGE_INTEGER *cb_read, ULARGE_INTEGER *cb_written) +{ + FIXME("(%p, %p, %s, %p, %p): stub.\n", iface, stream, wine_dbgstr_longlong(cb.QuadPart), cb_read, cb_written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_Commit(ISpMMSysAudio *iface, DWORD flags) +{ + FIXME("(%p, %#lx): stub.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_Revert(ISpMMSysAudio *iface) +{ + FIXME("(%p).\n", iface); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_LockRegion(ISpMMSysAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb, + DWORD lock_type) +{ + FIXME("(%p, %s, %s, %#lx): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart), + wine_dbgstr_longlong(cb.QuadPart), lock_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_UnlockRegion(ISpMMSysAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb, + DWORD lock_type) +{ + FIXME("(%p, %s, %s, %#lx): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart), + wine_dbgstr_longlong(cb.QuadPart), lock_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_Stat(ISpMMSysAudio *iface, STATSTG *statstg, DWORD flags) +{ + FIXME("(%p, %p, %#lx): stub.\n", iface, statstg, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_Clone(ISpMMSysAudio *iface, IStream **stream) +{ + FIXME("(%p, %p): stub.\n", iface, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetFormat(ISpMMSysAudio *iface, GUID *format, WAVEFORMATEX **wfx) +{ + FIXME("(%p, %p, %p): stub.\n", iface, format, wfx); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved) +{ + FIXME("(%p, %u, %s): stub.\n", iface, state, wine_dbgstr_longlong(reserved)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetFormat(ISpMMSysAudio *iface, const GUID *guid, const WAVEFORMATEX *wfx) +{ + FIXME("(%p, %s, %p): stub.\n", iface, debugstr_guid(guid), wfx); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetStatus(ISpMMSysAudio *iface, SPAUDIOSTATUS *status) +{ + FIXME("(%p, %p): stub.\n", iface, status); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetBufferInfo(ISpMMSysAudio *iface, const SPAUDIOBUFFERINFO *info) +{ + FIXME("(%p, %p): stub.\n", iface, info); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetBufferInfo(ISpMMSysAudio *iface, SPAUDIOBUFFERINFO *info) +{ + FIXME("(%p, %p): stub.\n", iface, info); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetDefaultFormat(ISpMMSysAudio *iface, GUID *guid, WAVEFORMATEX **wfx) +{ + FIXME("(%p, %p, %p): stub.\n", iface, guid, wfx); + + return E_NOTIMPL; +} + +static HANDLE WINAPI mmsysaudio_EventHandle(ISpMMSysAudio *iface) +{ + FIXME("(%p): stub.\n", iface); + + return NULL; +} + +static HRESULT WINAPI mmsysaudio_GetVolumeLevel(ISpMMSysAudio *iface, ULONG *level) +{ + FIXME("(%p, %p): stub.\n", iface, level); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetVolumeLevel(ISpMMSysAudio *iface, ULONG level) +{ + FIXME("(%p, %lu): stub.\n", iface, level); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetBufferNotifySize(ISpMMSysAudio *iface, ULONG *size) +{ + FIXME("(%p, %p): stub.\n", iface, size); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetBufferNotifySize(ISpMMSysAudio *iface, ULONG size) +{ + FIXME("(%p, %lu): stub.\n", iface, size); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetDeviceId(ISpMMSysAudio *iface, UINT *id) +{ + FIXME("(%p, %p): stub.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetDeviceId(ISpMMSysAudio *iface, UINT id) +{ + FIXME("(%p, %u): stub.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetMMHandle(ISpMMSysAudio *iface, void **handle) +{ + FIXME("(%p, %p): stub.\n", iface, handle); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_GetLineId(ISpMMSysAudio *iface, UINT *id) +{ + FIXME("(%p, %p): stub.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mmsysaudio_SetLineId(ISpMMSysAudio *iface, UINT id) +{ + FIXME("(%p, %u): stub.\n", iface, id); + + return E_NOTIMPL; +} + +static const ISpMMSysAudioVtbl mmsysaudio_vtbl = +{ + mmsysaudio_QueryInterface, + mmsysaudio_AddRef, + mmsysaudio_Release, + mmsysaudio_Read, + mmsysaudio_Write, + mmsysaudio_Seek, + mmsysaudio_SetSize, + mmsysaudio_CopyTo, + mmsysaudio_Commit, + mmsysaudio_Revert, + mmsysaudio_LockRegion, + mmsysaudio_UnlockRegion, + mmsysaudio_Stat, + mmsysaudio_Clone, + mmsysaudio_GetFormat, + mmsysaudio_SetState, + mmsysaudio_SetFormat, + mmsysaudio_GetStatus, + mmsysaudio_SetBufferInfo, + mmsysaudio_GetBufferInfo, + mmsysaudio_GetDefaultFormat, + mmsysaudio_EventHandle, + mmsysaudio_GetVolumeLevel, + mmsysaudio_SetVolumeLevel, + mmsysaudio_GetBufferNotifySize, + mmsysaudio_SetBufferNotifySize, + mmsysaudio_GetDeviceId, + mmsysaudio_SetDeviceId, + mmsysaudio_GetMMHandle, + mmsysaudio_GetLineId, + mmsysaudio_SetLineId +}; + +static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow_type flow) +{ + struct mmaudio *This; + HRESULT hr; + + if (flow != FLOW_OUT) + { + FIXME("flow %d not implemented.\n", flow); + return E_NOTIMPL; + } + + if (!(This = heap_alloc_zero(sizeof(*This)))) + return E_OUTOFMEMORY; + This->ISpEventSource_iface.lpVtbl = &event_source_vtbl; + This->ISpEventSink_iface.lpVtbl = &event_sink_vtbl; + This->ISpObjectWithToken_iface.lpVtbl = &objwithtoken_vtbl; + This->ISpMMSysAudio_iface.lpVtbl = &mmsysaudio_vtbl; + This->ref = 1; + + This->flow = flow; + This->token = NULL; + + hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); + + ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface); + return hr; +} + +HRESULT mmaudio_out_create(IUnknown *outer, REFIID iid, void **obj) +{ + return mmaudio_create(outer, iid, obj, FLOW_OUT); +} diff --git a/dlls/sapi/sapi_classes.idl b/dlls/sapi/sapi_classes.idl index bb580dde18e..14f24aa9c02 100644 --- a/dlls/sapi/sapi_classes.idl +++ b/dlls/sapi/sapi_classes.idl @@ -103,3 +103,18 @@ coclass SpFileStream interface ISpStream; [default] interface ISpeechFileStream; } + +[ + uuid(a8c680eb-3d32-11d2-9ee7-00c04f797396), + helpstring("SpMMAudioOut"), + progid("SAPI.SpMMAudioOut.1"), + vi_progid("SAPI.SpMMAudioOut"), + threading(both) +] +coclass SpMMAudioOut +{ + interface ISpEventSource; + interface ISpEventSink; + interface ISpObjectWithToken; + interface ISpMMSysAudio; +} diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index fcecafb450e..88b1a27516f 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -25,6 +25,7 @@ HRESULT file_stream_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_H HRESULT resource_manager_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT speech_stream_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT speech_voice_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; +HRESULT mmaudio_out_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; diff --git a/dlls/sapi/tests/Makefile.in b/dlls/sapi/tests/Makefile.in index 75c70d072d8..981bb828b8b 100644 --- a/dlls/sapi/tests/Makefile.in +++ b/dlls/sapi/tests/Makefile.in @@ -3,6 +3,7 @@ IMPORTS = ole32 user32 advapi32 C_SRCS = \ automation.c \ + mmaudio.c \ resource.c \ stream.c \ token.c \ diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c new file mode 100644 index 00000000000..5dd0d91ab1d --- /dev/null +++ b/dlls/sapi/tests/mmaudio.c @@ -0,0 +1,68 @@ +/* + * Speech API (SAPI) winmm audio tests. + * + * Copyright 2023 Shaun Ren 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 + */ + +#define COBJMACROS + +#include "sapiddk.h" +#include "sperror.h" + +#include "wine/test.h" + +static void test_interfaces(void) +{ + ISpMMSysAudio *mmaudio; + IUnknown *unk; + ISpEventSource *source; + ISpEventSink *sink; + ISpObjectWithToken *obj_with_token; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&mmaudio); + ok(hr == S_OK, "Failed to create ISpMMSysAudio interface: %#lx.\n", hr); + ISpMMSysAudio_Release(mmaudio); + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Failed to create IUnknown interface: %#lx.\n", hr); + IUnknown_Release(unk); + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpEventSource, (void **)&source); + ok(hr == S_OK, "Failed to create ISpEventSource interface: %#lx.\n", hr); + ISpEventSource_Release(source); + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpEventSink, (void **)&sink); + ok(hr == S_OK, "Failed to create ISpEventSink interface: %#lx.\n", hr); + ISpEventSink_Release(sink); + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectWithToken, (void **)&obj_with_token); + ok(hr == S_OK, "Failed to create ISpObjectWithToken interface: %#lx.\n", hr); + ISpObjectWithToken_Release(obj_with_token); +} + +START_TEST(mmaudio) +{ + CoInitialize(NULL); + test_interfaces(); + CoUninitialize(); +} From 2b1735e897d4c381b77d99fef6aca572fecc49f3 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 23:56:47 -0400 Subject: [PATCH 630/758] sapi: Implement ISpMMSysAudio::Get/SetDeviceId. (cherry picked from commit 454283ec2225bc2b64a643fe9977d842a7036a4a) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/Makefile.in | 1 + dlls/sapi/mmaudio.c | 32 ++++++++++++++++++++++++----- dlls/sapi/tests/Makefile.in | 2 +- dlls/sapi/tests/mmaudio.c | 40 +++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index ca64edec6dd..8a1ead0b78b 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -1,5 +1,6 @@ MODULE = sapi.dll IMPORTS = uuid ole32 user32 advapi32 +DELAYIMPORTS = winmm C_SRCS = \ automation.c \ diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index 684476f5c56..ec9c02cb210 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -47,6 +47,8 @@ struct mmaudio enum flow_type flow; ISpObjectToken *token; + UINT device_id; + CRITICAL_SECTION cs; }; static inline struct mmaudio *impl_from_ISpEventSource(ISpEventSource *iface) @@ -356,6 +358,7 @@ static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface) if (!ref) { if (This->token) ISpObjectToken_Release(This->token); + DeleteCriticalSection(&This->cs); heap_free(This); } @@ -531,16 +534,33 @@ static HRESULT WINAPI mmsysaudio_SetBufferNotifySize(ISpMMSysAudio *iface, ULONG static HRESULT WINAPI mmsysaudio_GetDeviceId(ISpMMSysAudio *iface, UINT *id) { - FIXME("(%p, %p): stub.\n", iface, id); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, id); + + if (!id) return E_POINTER; + + EnterCriticalSection(&This->cs); + *id = This->device_id; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI mmsysaudio_SetDeviceId(ISpMMSysAudio *iface, UINT id) { - FIXME("(%p, %u): stub.\n", iface, id); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); - return E_NOTIMPL; + TRACE("(%p, %u).\n", iface, id); + + if (id != WAVE_MAPPER && id >= waveOutGetNumDevs()) + return E_INVALIDARG; + + EnterCriticalSection(&This->cs); + This->device_id = id; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI mmsysaudio_GetMMHandle(ISpMMSysAudio *iface, void **handle) @@ -620,9 +640,11 @@ static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow This->flow = flow; This->token = NULL; + This->device_id = WAVE_MAPPER; - hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); + InitializeCriticalSection(&This->cs); + hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface); return hr; } diff --git a/dlls/sapi/tests/Makefile.in b/dlls/sapi/tests/Makefile.in index 981bb828b8b..ea14710194f 100644 --- a/dlls/sapi/tests/Makefile.in +++ b/dlls/sapi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = sapi.dll -IMPORTS = ole32 user32 advapi32 +IMPORTS = ole32 user32 advapi32 winmm C_SRCS = \ automation.c \ diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c index 5dd0d91ab1d..3816c0caf9c 100644 --- a/dlls/sapi/tests/mmaudio.c +++ b/dlls/sapi/tests/mmaudio.c @@ -60,9 +60,49 @@ static void test_interfaces(void) ISpObjectWithToken_Release(obj_with_token); } +static void test_device_id(void) +{ + ISpMMSysAudio *mmaudio; + UINT id, num_devs; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&mmaudio); + ok(hr == S_OK, "failed to create SpMMAudioOut instance: %#lx.\n", hr); + + id = 0xdeadbeef; + hr = ISpMMSysAudio_GetDeviceId(mmaudio, &id); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(id == WAVE_MAPPER, "got %#x.\n", id); + + hr = ISpMMSysAudio_SetDeviceId(mmaudio, WAVE_MAPPER); + ok(hr == S_OK, "got %#lx.\n", hr); + + num_devs = waveOutGetNumDevs(); + if (num_devs == 0) { + skip("no wave out devices.\n"); + ISpMMSysAudio_Release(mmaudio); + return; + } + + hr = ISpMMSysAudio_SetDeviceId(mmaudio, num_devs); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetDeviceId(mmaudio, 0); + ok(hr == S_OK || broken(hr == S_FALSE) /* Windows */, "got %#lx.\n", hr); + + id = 0xdeadbeef; + hr = ISpMMSysAudio_GetDeviceId(mmaudio, &id); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(id == 0, "got %u.\n", id); + + ISpMMSysAudio_Release(mmaudio); +} + START_TEST(mmaudio) { CoInitialize(NULL); test_interfaces(); + test_device_id(); CoUninitialize(); } From 81796a2e4818f8df436161daf4d47af4499ed1b0 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 15 May 2023 23:06:47 -0400 Subject: [PATCH 631/758] sapi: Add GUIDs SPDFID_Text/WaveFormatEx. (cherry picked from commit 615852a97fe73964973f465a8db7227232c2464e) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/mmaudio.c | 5 +++++ include/sapi.idl | 3 +++ 2 files changed, 8 insertions(+) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index ec9c02cb210..c9eb12bdcb5 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -29,12 +29,17 @@ #include "sapiddk.h" #include "sperror.h" +#include "initguid.h" + #include "wine/debug.h" #include "sapi_private.h" WINE_DEFAULT_DEBUG_CHANNEL(sapi); +DEFINE_GUID(SPDFID_Text, 0x7ceef9f9, 0x3d13, 0x11d2, 0x9e, 0xe7, 0x00, 0xc0, 0x4f, 0x79, 0x73, 0x96); +DEFINE_GUID(SPDFID_WaveFormatEx, 0xc31adbae, 0x527f, 0x4ff5, 0xa2, 0x30, 0xf6, 0x2b, 0xb6, 0x1f, 0xf7, 0x0c); + enum flow_type { FLOW_IN, FLOW_OUT }; struct mmaudio diff --git a/include/sapi.idl b/include/sapi.idl index 92a2881501c..16b8348d73b 100644 --- a/include/sapi.idl +++ b/include/sapi.idl @@ -426,6 +426,9 @@ typedef [hidden] enum SPSTREAMFORMAT SPSF_NUM_FORMATS } SPSTREAMFORMAT; +cpp_quote("EXTERN_C const GUID SPDFID_Text;") +cpp_quote("EXTERN_C const GUID SPDFID_WaveFormatEx;") + typedef unsigned short SPPHONEID; typedef [restricted, hidden] struct SPPHRASEELEMENT From 0352dad1f6dfcc3097695b45e8a4a74401c6eebc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 14:31:51 -0400 Subject: [PATCH 632/758] include: Add sperror error code SPERR_UNSUPPORTED_FORMAT. (cherry picked from commit b24c1abf623ce08370b70da9de93efd615a55e21) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- include/sperror.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sperror.h b/include/sperror.h index cd8ed9b948d..0e37ac91c87 100644 --- a/include/sperror.h +++ b/include/sperror.h @@ -47,6 +47,7 @@ #define SPERR_UNINITIALIZED 0x80045001 #define SPERR_ALREADY_INITIALIZED 0x80045002 +#define SPERR_UNSUPPORTED_FORMAT 0x80045003 #define SPERR_INVALID_FLAGS 0x80045004 #define SPERR_DEVICE_BUSY 0x80045006 #define SPERR_DEVICE_NOT_SUPPORTED 0x80045007 From 5bef131f8dd8f593763518e91f1436b719881b01 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 27 May 2023 16:26:34 -0400 Subject: [PATCH 633/758] sapi: Implement ISpMMSysAudio::Get/SetFormat. (cherry picked from commit 473a5462e583cefff3e276cdf1e16b953466baa0) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/mmaudio.c | 68 ++++++++++++++++++++++++++++++++++++--- dlls/sapi/tests/mmaudio.c | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index c9eb12bdcb5..690e540122b 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -53,6 +53,7 @@ struct mmaudio enum flow_type flow; ISpObjectToken *token; UINT device_id; + WAVEFORMATEX *wfx; CRITICAL_SECTION cs; }; @@ -363,6 +364,7 @@ static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface) if (!ref) { if (This->token) ISpObjectToken_Release(This->token); + heap_free(This->wfx); DeleteCriticalSection(&This->cs); heap_free(This); @@ -455,9 +457,26 @@ static HRESULT WINAPI mmsysaudio_Clone(ISpMMSysAudio *iface, IStream **stream) static HRESULT WINAPI mmsysaudio_GetFormat(ISpMMSysAudio *iface, GUID *format, WAVEFORMATEX **wfx) { - FIXME("(%p, %p, %p): stub.\n", iface, format, wfx); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %p).\n", iface, format, wfx); + + if (!format || !wfx) + return E_POINTER; + + EnterCriticalSection(&This->cs); + + if (!(*wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX) + This->wfx->cbSize))) + { + LeaveCriticalSection(&This->cs); + return E_OUTOFMEMORY; + } + *format = SPDFID_WaveFormatEx; + memcpy(*wfx, This->wfx, sizeof(WAVEFORMATEX) + This->wfx->cbSize); + + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved) @@ -469,9 +488,37 @@ static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE sta static HRESULT WINAPI mmsysaudio_SetFormat(ISpMMSysAudio *iface, const GUID *guid, const WAVEFORMATEX *wfx) { - FIXME("(%p, %s, %p): stub.\n", iface, debugstr_guid(guid), wfx); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); + MMRESULT res; + WAVEFORMATEX *new_wfx; - return E_NOTIMPL; + TRACE("(%p, %s, %p).\n", iface, debugstr_guid(guid), wfx); + + if (!guid || !wfx || !IsEqualGUID(guid, &SPDFID_WaveFormatEx)) + return E_INVALIDARG; + + EnterCriticalSection(&This->cs); + + /* Determine whether the device supports the requested format. */ + res = waveOutOpen(NULL, This->device_id, wfx, 0, 0, WAVE_FORMAT_QUERY); + if (res != MMSYSERR_NOERROR) + { + LeaveCriticalSection(&This->cs); + return res == WAVERR_BADFORMAT ? SPERR_UNSUPPORTED_FORMAT : SPERR_GENERIC_MMSYS_ERROR; + } + + if (!(new_wfx = heap_alloc(sizeof(*wfx) + wfx->cbSize))) + { + LeaveCriticalSection(&This->cs); + return E_OUTOFMEMORY; + } + memcpy(new_wfx, wfx, sizeof(*wfx) + wfx->cbSize); + heap_free(This->wfx); + This->wfx = new_wfx; + + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI mmsysaudio_GetStatus(ISpMMSysAudio *iface, SPAUDIOSTATUS *status) @@ -647,6 +694,19 @@ static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow This->token = NULL; This->device_id = WAVE_MAPPER; + if (!(This->wfx = heap_alloc(sizeof(*This->wfx)))) + { + heap_free(This); + return E_OUTOFMEMORY; + } + This->wfx->wFormatTag = WAVE_FORMAT_PCM; + This->wfx->nChannels = 1; + This->wfx->nSamplesPerSec = 22050; + This->wfx->nAvgBytesPerSec = 22050 * 2; + This->wfx->nBlockAlign = 2; + This->wfx->wBitsPerSample = 16; + This->wfx->cbSize = 0; + InitializeCriticalSection(&This->cs); hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c index 3816c0caf9c..8611242e2b7 100644 --- a/dlls/sapi/tests/mmaudio.c +++ b/dlls/sapi/tests/mmaudio.c @@ -22,9 +22,13 @@ #include "sapiddk.h" #include "sperror.h" +#include "initguid.h" #include "wine/test.h" +DEFINE_GUID(SPDFID_Text, 0x7ceef9f9, 0x3d13, 0x11d2, 0x9e, 0xe7, 0x00, 0xc0, 0x4f, 0x79, 0x73, 0x96); +DEFINE_GUID(SPDFID_WaveFormatEx, 0xc31adbae, 0x527f, 0x4ff5, 0xa2, 0x30, 0xf6, 0x2b, 0xb6, 0x1f, 0xf7, 0x0c); + static void test_interfaces(void) { ISpMMSysAudio *mmaudio; @@ -99,10 +103,66 @@ static void test_device_id(void) ISpMMSysAudio_Release(mmaudio); } +static void test_formats(void) +{ + ISpMMSysAudio *mmaudio; + GUID fmtid; + WAVEFORMATEX *wfx; + WAVEFORMATEX wfx2; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&mmaudio); + ok(hr == S_OK, "failed to create SpMMAudioOut instance: %#lx.\n", hr); + + wfx = NULL; + hr = ISpMMSysAudio_GetFormat(mmaudio, &fmtid, &wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(IsEqualGUID(&fmtid, &SPDFID_WaveFormatEx), "got %s.\n", wine_dbgstr_guid(&fmtid)); + ok(wfx != NULL, "wfx == NULL.\n"); + ok(wfx->wFormatTag == WAVE_FORMAT_PCM, "got %u.\n", wfx->wFormatTag); + ok(wfx->nChannels == 1, "got %u.\n", wfx->nChannels); + ok(wfx->nSamplesPerSec == 22050, "got %lu.\n", wfx->nSamplesPerSec); + ok(wfx->nAvgBytesPerSec == 22050 * 2, "got %lu.\n", wfx->nAvgBytesPerSec); + ok(wfx->nBlockAlign == 2, "got %u.\n", wfx->nBlockAlign); + ok(wfx->wBitsPerSample == 16, "got %u.\n", wfx->wBitsPerSample); + ok(wfx->cbSize == 0, "got %u.\n", wfx->cbSize); + CoTaskMemFree(wfx); + + hr = ISpMMSysAudio_SetFormat(mmaudio, NULL, NULL); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetFormat(mmaudio, &SPDFID_Text, NULL); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetFormat(mmaudio, &SPDFID_WaveFormatEx, NULL); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + if (waveOutGetNumDevs() == 0) { + skip("no wave out devices.\n"); + ISpMMSysAudio_Release(mmaudio); + return; + } + + wfx2.wFormatTag = WAVE_FORMAT_PCM; + wfx2.nChannels = 2; + wfx2.nSamplesPerSec = 16000; + wfx2.nAvgBytesPerSec = 16000 * 2 * 2; + wfx2.nBlockAlign = 2 * 2; + wfx2.wBitsPerSample = 16; + wfx2.cbSize = 0; + + hr = ISpMMSysAudio_SetFormat(mmaudio, &SPDFID_WaveFormatEx, &wfx2); + ok(hr == S_OK, "got %#lx.\n", hr); + + ISpMMSysAudio_Release(mmaudio); +} + START_TEST(mmaudio) { CoInitialize(NULL); test_interfaces(); test_device_id(); + test_formats(); CoUninitialize(); } From 066c8dfdb5163dd941a9c27199f1c7969a6d7e5e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 27 May 2023 17:20:03 -0400 Subject: [PATCH 634/758] sapi: Partially implement ISpMMSysAudio::SetState. (cherry picked from commit 107d95165a0902828124bb1d9bf5adda987c9810) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/mmaudio.c | 73 +++++++++++++++++++++++++++++++++++++-- dlls/sapi/tests/mmaudio.c | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index 690e540122b..20e6cc5064f 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -53,7 +53,13 @@ struct mmaudio enum flow_type flow; ISpObjectToken *token; UINT device_id; + SPAUDIOSTATE state; WAVEFORMATEX *wfx; + union + { + HWAVEIN in; + HWAVEOUT out; + } hwave; CRITICAL_SECTION cs; }; @@ -363,6 +369,8 @@ static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface) if (!ref) { + ISpMMSysAudio_SetState(iface, SPAS_CLOSED, 0); + if (This->token) ISpObjectToken_Release(This->token); heap_free(This->wfx); DeleteCriticalSection(&This->cs); @@ -481,9 +489,45 @@ static HRESULT WINAPI mmsysaudio_GetFormat(ISpMMSysAudio *iface, GUID *format, W static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved) { - FIXME("(%p, %u, %s): stub.\n", iface, state, wine_dbgstr_longlong(reserved)); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("(%p, %u, %s).\n", iface, state, wine_dbgstr_longlong(reserved)); + + if (state != SPAS_CLOSED && state != SPAS_RUN) + { + FIXME("state %#x not implemented.\n", state); + return E_NOTIMPL; + } + + EnterCriticalSection(&This->cs); + + if (This->state == state) + goto done; + + if (This->state == SPAS_CLOSED) + { + if (waveOutOpen(&This->hwave.out, This->device_id, This->wfx, 0, 0, 0) != MMSYSERR_NOERROR) + { + hr = SPERR_GENERIC_MMSYS_ERROR; + goto done; + } + } + + if (state == SPAS_CLOSED && This->state != SPAS_CLOSED) + { + if (waveOutClose(This->hwave.out) != MMSYSERR_NOERROR) + { + hr = SPERR_GENERIC_MMSYS_ERROR; + goto done; + } + } + + This->state = state; + +done: + LeaveCriticalSection(&This->cs); + return hr; } static HRESULT WINAPI mmsysaudio_SetFormat(ISpMMSysAudio *iface, const GUID *guid, const WAVEFORMATEX *wfx) @@ -499,6 +543,18 @@ static HRESULT WINAPI mmsysaudio_SetFormat(ISpMMSysAudio *iface, const GUID *gui EnterCriticalSection(&This->cs); + if (!memcmp(wfx, This->wfx, sizeof(*wfx)) && !memcmp(wfx + 1, This->wfx + 1, wfx->cbSize)) + { + LeaveCriticalSection(&This->cs); + return S_OK; + } + + if (This->state != SPAS_CLOSED) + { + LeaveCriticalSection(&This->cs); + return SPERR_DEVICE_BUSY; + } + /* Determine whether the device supports the requested format. */ res = waveOutOpen(NULL, This->device_id, wfx, 0, 0, WAVE_FORMAT_QUERY); if (res != MMSYSERR_NOERROR) @@ -609,7 +665,19 @@ static HRESULT WINAPI mmsysaudio_SetDeviceId(ISpMMSysAudio *iface, UINT id) return E_INVALIDARG; EnterCriticalSection(&This->cs); + + if (id == This->device_id) + { + LeaveCriticalSection(&This->cs); + return S_OK; + } + if (This->state != SPAS_CLOSED) + { + LeaveCriticalSection(&This->cs); + return SPERR_DEVICE_BUSY; + } This->device_id = id; + LeaveCriticalSection(&This->cs); return S_OK; @@ -693,6 +761,7 @@ static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow This->flow = flow; This->token = NULL; This->device_id = WAVE_MAPPER; + This->state = SPAS_CLOSED; if (!(This->wfx = heap_alloc(sizeof(*This->wfx)))) { diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c index 8611242e2b7..85a3570f490 100644 --- a/dlls/sapi/tests/mmaudio.c +++ b/dlls/sapi/tests/mmaudio.c @@ -158,11 +158,73 @@ static void test_formats(void) ISpMMSysAudio_Release(mmaudio); } +static void test_audio_out(void) +{ + ISpMMSysAudio *mmaudio; + GUID fmtid; + WAVEFORMATEX *wfx = NULL; + UINT devid; + HRESULT hr; + + if (waveOutGetNumDevs() == 0) { + skip("no wave out devices.\n"); + return; + } + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&mmaudio); + ok(hr == S_OK, "failed to create SPMMAudioOut instance: %#lx.\n", hr); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_GetFormat(mmaudio, &fmtid, &wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(IsEqualGUID(&fmtid, &SPDFID_WaveFormatEx), "got %s.\n", wine_dbgstr_guid(&fmtid)); + ok(wfx != NULL, "wfx == NULL.\n"); + + hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetDeviceId(mmaudio, WAVE_MAPPER); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_RUN, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetDeviceId(mmaudio, WAVE_MAPPER); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetDeviceId(mmaudio, 0); + ok(hr == SPERR_DEVICE_BUSY, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + + wfx->nChannels = wfx->nChannels == 1 ? 2 : 1; + wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nChannels * wfx->wBitsPerSample / 8; + wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; + hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, wfx); + ok(hr == SPERR_DEVICE_BUSY, "got %#lx.\n", hr); + + devid = 0xdeadbeef; + hr = ISpMMSysAudio_GetDeviceId(mmaudio, &devid); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(devid == WAVE_MAPPER, "got %#x.\n", devid); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + CoTaskMemFree(wfx); + ISpMMSysAudio_Release(mmaudio); +} + START_TEST(mmaudio) { CoInitialize(NULL); test_interfaces(); test_device_id(); test_formats(); + test_audio_out(); CoUninitialize(); } From bcc7abcb561e4e9dee716f43efd2ec10e5104715 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 30 May 2023 23:29:22 -0400 Subject: [PATCH 635/758] sapi: Free completed buffers asynchronously in SpMMAudio. Also introduce async helpers. The buffers cannot be freed directly in wave_out_proc, because calling waveOut related functions in the callback could cause a deadlock. (cherry picked from commit 7bced2878a55d388beaeae76d58d693391e4de24) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/Makefile.in | 1 + dlls/sapi/async.c | 175 +++++++++++++++++++++++++++++++++++++++ dlls/sapi/mmaudio.c | 78 ++++++++++++++++- dlls/sapi/sapi_private.h | 24 ++++++ 4 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 dlls/sapi/async.c diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index 8a1ead0b78b..4229320e473 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -3,6 +3,7 @@ IMPORTS = uuid ole32 user32 advapi32 DELAYIMPORTS = winmm C_SRCS = \ + async.c \ automation.c \ main.c \ mmaudio.c \ diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c new file mode 100644 index 00000000000..57ae89ad723 --- /dev/null +++ b/dlls/sapi/async.c @@ -0,0 +1,175 @@ +/* + * Speech API (SAPI) async helper implementation. + * + * Copyright 2023 Shaun Ren 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 + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "wine/heap.h" +#include "wine/list.h" +#include "wine/debug.h" + +#include "sapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sapi); + +static struct async_task *async_dequeue_task(struct async_queue *queue) +{ + struct async_task *task = NULL; + struct list *head; + + EnterCriticalSection(&queue->cs); + if ((head = list_head(&queue->tasks))) + { + task = LIST_ENTRY(head, struct async_task, entry); + list_remove(head); + } + LeaveCriticalSection(&queue->cs); + + return task; +} + +void async_empty_queue(struct async_queue *queue) +{ + struct async_task *task, *next; + + EnterCriticalSection(&queue->cs); + LIST_FOR_EACH_ENTRY_SAFE(task, next, &queue->tasks, struct async_task, entry) + { + list_remove(&task->entry); + heap_free(task); + } + LeaveCriticalSection(&queue->cs); + + SetEvent(queue->empty); +} + +static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx) +{ + struct async_queue *queue = ctx; + HANDLE handles[2] = { queue->cancel, queue->wait }; + DWORD ret; + + SetEvent(queue->ready); + + for (;;) + { + ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + if (ret == WAIT_OBJECT_0) + goto cancel; + else if (ret == WAIT_OBJECT_0 + 1) + { + struct async_task *task; + + while ((task = async_dequeue_task(queue))) + { + ResetEvent(queue->empty); + task->proc(task); + heap_free(task); + if (WaitForSingleObject(queue->cancel, 0) == WAIT_OBJECT_0) + goto cancel; + } + + SetEvent(queue->empty); + } + else + ERR("WaitForMultipleObjects failed: %#lx.\n", ret); + } + +cancel: + async_empty_queue(queue); + TRACE("cancelled.\n"); + SetEvent(queue->ready); +} + +HRESULT async_start_queue(struct async_queue *queue) +{ + HRESULT hr; + + if (queue->init) + return S_OK; + + InitializeCriticalSection(&queue->cs); + list_init(&queue->tasks); + + if (!(queue->wait = CreateEventW(NULL, FALSE, FALSE, NULL)) || + !(queue->ready = CreateEventW(NULL, FALSE, FALSE, NULL)) || + !(queue->cancel = CreateEventW(NULL, FALSE, FALSE, NULL)) || + !(queue->empty = CreateEventW(NULL, TRUE, TRUE, NULL))) + goto fail; + + queue->init = TRUE; + + if (!TrySubmitThreadpoolCallback(async_worker, queue, NULL)) + goto fail; + + WaitForSingleObject(queue->ready, INFINITE); + return S_OK; + +fail: + hr = HRESULT_FROM_WIN32(GetLastError()); + DeleteCriticalSection(&queue->cs); + if (queue->wait) CloseHandle(queue->wait); + if (queue->ready) CloseHandle(queue->ready); + if (queue->cancel) CloseHandle(queue->cancel); + if (queue->empty) CloseHandle(queue->empty); + memset(queue, 0, sizeof(*queue)); + return hr; +} + +void async_cancel_queue(struct async_queue *queue) +{ + if (!queue->init) return; + + SetEvent(queue->cancel); + WaitForSingleObject(queue->ready, INFINITE); + + DeleteCriticalSection(&queue->cs); + CloseHandle(queue->wait); + CloseHandle(queue->ready); + CloseHandle(queue->cancel); + CloseHandle(queue->empty); + + memset(queue, 0, sizeof(*queue)); +} + +HRESULT async_queue_task(struct async_queue *queue, struct async_task *task) +{ + HRESULT hr; + + if (FAILED(hr = async_start_queue(queue))) + return hr; + + EnterCriticalSection(&queue->cs); + list_add_tail(&queue->tasks, &task->entry); + LeaveCriticalSection(&queue->cs); + + SetEvent(queue->wait); + + return S_OK; +} + +void async_wait_queue_empty(struct async_queue *queue, DWORD timeout) +{ + if (!queue->init) return; + WaitForSingleObject(queue->empty, timeout); +} diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index 20e6cc5064f..add670ff3dc 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -60,7 +60,12 @@ struct mmaudio HWAVEIN in; HWAVEOUT out; } hwave; + HANDLE event; + struct async_queue queue; CRITICAL_SECTION cs; + + size_t pending_buf_count; + CRITICAL_SECTION pending_cs; }; static inline struct mmaudio *impl_from_ISpEventSource(ISpEventSource *iface) @@ -371,8 +376,13 @@ static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface) { ISpMMSysAudio_SetState(iface, SPAS_CLOSED, 0); + async_wait_queue_empty(&This->queue, INFINITE); + async_cancel_queue(&This->queue); + if (This->token) ISpObjectToken_Release(This->token); heap_free(This->wfx); + CloseHandle(This->event); + DeleteCriticalSection(&This->pending_cs); DeleteCriticalSection(&This->cs); heap_free(This); @@ -487,6 +497,57 @@ static HRESULT WINAPI mmsysaudio_GetFormat(ISpMMSysAudio *iface, GUID *format, W return S_OK; } +struct free_buf_task +{ + struct async_task task; + struct mmaudio *audio; + WAVEHDR *buf; +}; + +static void free_out_buf_proc(struct async_task *task) +{ + struct free_buf_task *fbt = (struct free_buf_task *)task; + size_t buf_count; + + TRACE("(%p).\n", task); + + waveOutUnprepareHeader(fbt->audio->hwave.out, fbt->buf, sizeof(WAVEHDR)); + heap_free(fbt->buf); + + EnterCriticalSection(&fbt->audio->pending_cs); + buf_count = --fbt->audio->pending_buf_count; + LeaveCriticalSection(&fbt->audio->pending_cs); + if (!buf_count) + SetEvent(fbt->audio->event); + TRACE("pending_buf_count = %Iu.\n", buf_count); +} + +static void CALLBACK wave_out_proc(HWAVEOUT hwo, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2) +{ + struct mmaudio *This = (struct mmaudio *)instance; + struct free_buf_task *task; + + TRACE("(%p, %#x, %08Ix, %08Ix, %08Ix).\n", hwo, msg, instance, param1, param2); + + switch (msg) + { + case WOM_DONE: + if (!(task = heap_alloc(sizeof(*task)))) + { + ERR("failed to allocate free_buf_task.\n"); + break; + } + task->task.proc = free_out_buf_proc; + task->audio = This; + task->buf = (WAVEHDR *)param1; + async_queue_task(&This->queue, (struct async_task *)task); + break; + + default: + break; + } +} + static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved) { struct mmaudio *This = impl_from_ISpMMSysAudio(iface); @@ -507,7 +568,14 @@ static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE sta if (This->state == SPAS_CLOSED) { - if (waveOutOpen(&This->hwave.out, This->device_id, This->wfx, 0, 0, 0) != MMSYSERR_NOERROR) + if (FAILED(hr = async_start_queue(&This->queue))) + { + ERR("Failed to start async queue: %#lx.\n", hr); + goto done; + } + + if (waveOutOpen(&This->hwave.out, This->device_id, This->wfx, (DWORD_PTR)wave_out_proc, + (DWORD_PTR)This, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { hr = SPERR_GENERIC_MMSYS_ERROR; goto done; @@ -516,6 +584,10 @@ static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE sta if (state == SPAS_CLOSED && This->state != SPAS_CLOSED) { + waveOutReset(This->hwave.out); + /* Wait until all buffers are freed. */ + WaitForSingleObject(This->event, INFINITE); + if (waveOutClose(This->hwave.out) != MMSYSERR_NOERROR) { hr = SPERR_GENERIC_MMSYS_ERROR; @@ -776,7 +848,11 @@ static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow This->wfx->wBitsPerSample = 16; This->wfx->cbSize = 0; + This->pending_buf_count = 0; + This->event = CreateEventW(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&This->cs); + InitializeCriticalSection(&This->pending_cs); hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj); ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface); diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 88b1a27516f..d0cd7669623 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -19,6 +19,30 @@ */ #include "wine/heap.h" +#include "wine/list.h" + +struct async_task +{ + struct list entry; + void (*proc)(struct async_task *); +}; + +struct async_queue +{ + BOOL init; + HANDLE wait; + HANDLE ready; + HANDLE empty; + HANDLE cancel; + struct list tasks; + CRITICAL_SECTION cs; +}; + +HRESULT async_start_queue(struct async_queue *queue); +void async_empty_queue(struct async_queue *queue); +void async_cancel_queue(struct async_queue *queue); +HRESULT async_queue_task(struct async_queue *queue, struct async_task *task); +void async_wait_queue_empty(struct async_queue *queue, DWORD timeout); HRESULT data_key_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT file_stream_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; From 506d0071bf5ed723eb996f359ae50a719265cbd1 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 30 May 2023 23:58:32 -0400 Subject: [PATCH 636/758] sapi: Implement ISpMMSysAudio::Write. (cherry picked from commit ef730b6e41f04d2810bbc6cc869d9fdf1d789c1d) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/mmaudio.c | 53 +++++++++++++++++++++++++++++++++++-- dlls/sapi/tests/mmaudio.c | 55 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index add670ff3dc..dadd767b9a1 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -400,9 +400,58 @@ static HRESULT WINAPI mmsysaudio_Read(ISpMMSysAudio *iface, void *pv, ULONG cb, static HRESULT WINAPI mmsysaudio_Write(ISpMMSysAudio *iface, const void *pv, ULONG cb, ULONG *cb_written) { - FIXME("(%p, %p, %lu, %p): stub.\n", iface, pv, cb, cb_written); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); + HRESULT hr = S_OK; + WAVEHDR *buf; - return E_NOTIMPL; + TRACE("(%p, %p, %lu, %p).\n", iface, pv, cb, cb_written); + + if (This->flow != FLOW_OUT) + return STG_E_ACCESSDENIED; + + if (cb_written) + *cb_written = 0; + + EnterCriticalSection(&This->cs); + + if (This->state == SPAS_CLOSED || This->state == SPAS_STOP) + { + LeaveCriticalSection(&This->cs); + return SP_AUDIO_STOPPED; + } + + if (!(buf = heap_alloc(sizeof(WAVEHDR) + cb))) + { + LeaveCriticalSection(&This->cs); + return E_OUTOFMEMORY; + } + memcpy((char *)(buf + 1), pv, cb); + buf->lpData = (char *)(buf + 1); + buf->dwBufferLength = cb; + buf->dwFlags = 0; + + if (waveOutPrepareHeader(This->hwave.out, buf, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) + { + LeaveCriticalSection(&This->cs); + heap_free(buf); + return E_FAIL; + } + + waveOutWrite(This->hwave.out, buf, sizeof(WAVEHDR)); + + EnterCriticalSection(&This->pending_cs); + ++This->pending_buf_count; + TRACE("pending_buf_count = %Iu\n", This->pending_buf_count); + LeaveCriticalSection(&This->pending_cs); + + ResetEvent(This->event); + + LeaveCriticalSection(&This->cs); + + if (cb_written) + *cb_written = cb; + + return hr; } static HRESULT WINAPI mmsysaudio_Seek(ISpMMSysAudio *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos) diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c index 85a3570f490..38df432c181 100644 --- a/dlls/sapi/tests/mmaudio.c +++ b/dlls/sapi/tests/mmaudio.c @@ -163,7 +163,10 @@ static void test_audio_out(void) ISpMMSysAudio *mmaudio; GUID fmtid; WAVEFORMATEX *wfx = NULL; + WAVEFORMATEX wfx2; UINT devid; + char *buf = NULL; + ULONG written; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -182,6 +185,8 @@ static void test_audio_out(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(IsEqualGUID(&fmtid, &SPDFID_WaveFormatEx), "got %s.\n", wine_dbgstr_guid(&fmtid)); ok(wfx != NULL, "wfx == NULL.\n"); + ok(wfx->wFormatTag == WAVE_FORMAT_PCM, "got %u.\n", wfx->wFormatTag); + ok(wfx->cbSize == 0, "got %u.\n", wfx->cbSize); hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, wfx); ok(hr == S_OK, "got %#lx.\n", hr); @@ -201,10 +206,12 @@ static void test_audio_out(void) hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, wfx); ok(hr == S_OK, "got %#lx.\n", hr); - wfx->nChannels = wfx->nChannels == 1 ? 2 : 1; - wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nChannels * wfx->wBitsPerSample / 8; - wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; - hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, wfx); + memcpy(&wfx2, wfx, sizeof(wfx2)); + wfx2.nChannels = wfx->nChannels == 1 ? 2 : 1; + wfx2.nAvgBytesPerSec = wfx2.nSamplesPerSec * wfx2.nChannels * wfx2.wBitsPerSample / 8; + wfx2.nBlockAlign = wfx2.nChannels * wfx2.wBitsPerSample / 8; + + hr = ISpMMSysAudio_SetFormat(mmaudio, &fmtid, &wfx2); ok(hr == SPERR_DEVICE_BUSY, "got %#lx.\n", hr); devid = 0xdeadbeef; @@ -215,7 +222,47 @@ static void test_audio_out(void) hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); ok(hr == S_OK, "got %#lx.\n", hr); + buf = calloc(1, wfx->nAvgBytesPerSec); + ok(buf != NULL, "failed to allocate buffer.\n"); + + hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec, NULL); + ok(hr == SP_AUDIO_STOPPED, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_STOP, 0); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + if (hr == S_OK) + { + hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec, NULL); + ok(hr == SP_AUDIO_STOPPED, "got %#lx.\n", hr); + } + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_RUN, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + Sleep(200); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_RUN, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + written = 0xdeadbeef; + hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec * 200 / 1000, &written); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(written == wfx->nAvgBytesPerSec * 200 / 1000, "got %lu.\n", written); + + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + CoTaskMemFree(wfx); + free(buf); ISpMMSysAudio_Release(mmaudio); } From c3a1bb64d1c60475d1fcfbb71c0e3aa379a89505 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 31 May 2023 00:09:50 -0400 Subject: [PATCH 637/758] sapi: Implement ISpMMSysAudio::EventHandle. (cherry picked from commit 7cd77b2a7890e6fbe97f52fe852e7dfa4e403570) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/mmaudio.c | 6 ++++-- dlls/sapi/tests/mmaudio.c | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index dadd767b9a1..d99bcb13672 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -728,9 +728,11 @@ static HRESULT WINAPI mmsysaudio_GetDefaultFormat(ISpMMSysAudio *iface, GUID *gu static HANDLE WINAPI mmsysaudio_EventHandle(ISpMMSysAudio *iface) { - FIXME("(%p): stub.\n", iface); + struct mmaudio *This = impl_from_ISpMMSysAudio(iface); - return NULL; + TRACE("(%p).\n", iface); + + return This->event; } static HRESULT WINAPI mmsysaudio_GetVolumeLevel(ISpMMSysAudio *iface, ULONG *level) diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c index 38df432c181..7c72fb112bf 100644 --- a/dlls/sapi/tests/mmaudio.c +++ b/dlls/sapi/tests/mmaudio.c @@ -167,6 +167,8 @@ static void test_audio_out(void) UINT devid; char *buf = NULL; ULONG written; + DWORD start, end; + HANDLE event = NULL; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -258,6 +260,20 @@ static void test_audio_out(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(written == wfx->nAvgBytesPerSec * 200 / 1000, "got %lu.\n", written); + hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec * 200 / 1000, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + start = GetTickCount(); + + event = ISpMMSysAudio_EventHandle(mmaudio); + ok(event != NULL, "event == NULL.\n"); + + hr = WaitForSingleObject(event, 1000); + ok(hr == WAIT_OBJECT_0, "got %#lx.\n", hr); + + end = GetTickCount(); + ok(end - start <= 500, "waited for %lu ms.\n", end - start); + hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); ok(hr == S_OK, "got %#lx.\n", hr); From 6597dbf067212094637e8c2550ac9b1f87ce82e3 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 20 Jun 2023 23:15:28 -0400 Subject: [PATCH 638/758] sapi: Implement ISpVoice::SetOutput. (cherry picked from commit 8c6bb3caee22c8dbaf157b4f2d608ff9a1ac3740) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 30 +++++++++++++++++++++++ dlls/sapi/tts.c | 56 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 8303dfc6ebc..5d0cfd1bc1b 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -93,9 +93,39 @@ static void test_interfaces(void) ISpeechVoice_Release(speech_voice); } +static void test_spvoice(void) +{ + ISpVoice *voice; + ISpMMSysAudio *audio_out; + HRESULT hr; + + if (waveOutGetNumDevs() == 0) { + skip("no wave out devices.\n"); + return; + } + + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpVoice, (void **)&voice); + ok(hr == S_OK, "Failed to create SpVoice: %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, NULL, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&audio_out); + ok(hr == S_OK, "Failed to create SpMMAudioOut: %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE); + todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr); + + ISpVoice_Release(voice); + ISpMMSysAudio_Release(audio_out); +} + START_TEST(tts) { CoInitialize(NULL); test_interfaces(); + test_spvoice(); CoUninitialize(); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 9f60c70e6c4..bebda86f09d 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -40,6 +40,9 @@ struct speech_voice ISpVoice ISpVoice_iface; IConnectionPointContainer IConnectionPointContainer_iface; LONG ref; + + ISpStreamFormat *output; + CRITICAL_SECTION cs; }; static inline struct speech_voice *impl_from_ISpeechVoice(ISpeechVoice *iface) @@ -102,6 +105,9 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) if (!ref) { + if (This->output) ISpStreamFormat_Release(This->output); + DeleteCriticalSection(&This->cs); + heap_free(This); } @@ -515,11 +521,51 @@ static HRESULT WINAPI spvoice_GetInfo(ISpVoice *iface, SPEVENTSOURCEINFO *info) return E_NOTIMPL; } -static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL changes) +static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL allow_format_changes) { - FIXME("(%p, %p, %d): stub.\n", iface, unk, changes); + struct speech_voice *This = impl_from_ISpVoice(iface); + ISpStreamFormat *stream = NULL; + ISpObjectToken *token = NULL; + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %p, %d).\n", iface, unk, allow_format_changes); + + if (!allow_format_changes) + FIXME("ignoring allow_format_changes = FALSE.\n"); + + if (!unk) + { + /* TODO: Create the default SpAudioOut token here once SpMMAudioEnum is implemented. */ + if (FAILED(hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpStreamFormat, (void **)&stream))) + return hr; + } + else + { + if (FAILED(IUnknown_QueryInterface(unk, &IID_ISpStreamFormat, (void **)&stream))) + { + if (FAILED(IUnknown_QueryInterface(unk, &IID_ISpObjectToken, (void **)&token))) + return E_INVALIDARG; + } + } + + if (!stream) + { + hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpStreamFormat, (void **)&stream); + ISpObjectToken_Release(token); + if (FAILED(hr)) + return hr; + } + + EnterCriticalSection(&This->cs); + + if (This->output) + ISpStreamFormat_Release(This->output); + This->output = stream; + + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetOutputObjectToken(ISpVoice *iface, ISpObjectToken **token) @@ -798,6 +844,10 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->IConnectionPointContainer_iface.lpVtbl = &container_vtbl; This->ref = 1; + This->output = NULL; + + InitializeCriticalSection(&This->cs); + hr = ISpeechVoice_QueryInterface(&This->ISpeechVoice_iface, iid, obj); ISpeechVoice_Release(&This->ISpeechVoice_iface); From c8811fb004e6cebc23a7fe945248b9e90378bd7d Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 21 Jun 2023 01:29:55 -0400 Subject: [PATCH 639/758] sapi: Implement ISpVoice::Set/GetVoice. (cherry picked from commit 17635423938d3041b806506f1c4a9caff4dd1018) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 31 +++++++++++++++ dlls/sapi/tts.c | 93 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 5d0cfd1bc1b..5141bc27b8c 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -97,6 +97,9 @@ static void test_spvoice(void) { ISpVoice *voice; ISpMMSysAudio *audio_out; + ISpObjectTokenCategory *token_cat; + ISpObjectToken *token; + WCHAR *token_id = NULL, *default_token_id = NULL; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -118,6 +121,34 @@ static void test_spvoice(void) hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE); todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr); + hr = ISpVoice_SetVoice(voice, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpVoice_GetVoice(voice, &token); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + if (SUCCEEDED(hr)) + { + hr = ISpObjectToken_GetId(token, &token_id); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenCategory, (void **)&token_cat); + ok(hr == S_OK, "Failed to create SpObjectTokenCategory: %#lx.\n", hr); + + hr = ISpObjectTokenCategory_SetId(token_cat, SPCAT_VOICES, FALSE); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpObjectTokenCategory_GetDefaultTokenId(token_cat, &default_token_id); + ok(hr == S_OK, "got %#lx.\n", hr); + + ok(!wcscmp(token_id, default_token_id), "token_id != default_token_id\n"); + + CoTaskMemFree(token_id); + CoTaskMemFree(default_token_id); + ISpObjectToken_Release(token); + ISpObjectTokenCategory_Release(token_cat); + } + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index bebda86f09d..b7af24790e0 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -42,6 +42,7 @@ struct speech_voice LONG ref; ISpStreamFormat *output; + ISpTTSEngine *engine; CRITICAL_SECTION cs; }; @@ -60,6 +61,41 @@ static inline struct speech_voice *impl_from_IConnectionPointContainer(IConnecti return CONTAINING_RECORD(iface, struct speech_voice, IConnectionPointContainer_iface); } +static HRESULT create_default_token(const WCHAR *cat_id, ISpObjectToken **token) +{ + ISpObjectTokenCategory *cat; + WCHAR *default_token_id = NULL; + HRESULT hr; + + TRACE("(%s, %p).\n", debugstr_w(cat_id), token); + + if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenCategory, (void **)&cat))) + return hr; + + if (FAILED(hr = ISpObjectTokenCategory_SetId(cat, cat_id, FALSE)) || + FAILED(hr = ISpObjectTokenCategory_GetDefaultTokenId(cat, &default_token_id))) + { + ISpObjectTokenCategory_Release(cat); + return hr; + } + ISpObjectTokenCategory_Release(cat); + + if (FAILED(hr = CoCreateInstance(&CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)token))) + goto done; + + if (FAILED(hr = ISpObjectToken_SetId(*token, NULL, default_token_id, FALSE))) + { + ISpObjectToken_Release(*token); + *token = NULL; + } + +done: + CoTaskMemFree(default_token_id); + return hr; +} + /* ISpeechVoice interface */ static HRESULT WINAPI speech_voice_QueryInterface(ISpeechVoice *iface, REFIID iid, void **obj) { @@ -106,6 +142,7 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) if (!ref) { if (This->output) ISpStreamFormat_Release(This->output); + if (This->engine) ISpTTSEngine_Release(This->engine); DeleteCriticalSection(&This->cs); heap_free(This); @@ -598,16 +635,63 @@ static HRESULT WINAPI spvoice_Resume(ISpVoice *iface) static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) { - FIXME("(%p, %p): stub.\n", iface, token); + struct speech_voice *This = impl_from_ISpVoice(iface); + ISpTTSEngine *engine; + HRESULT hr; - return E_NOTIMPL; + + TRACE("(%p, %p).\n", iface, token); + + if (!token) + { + if (FAILED(hr = create_default_token(SPCAT_VOICES, &token))) + return hr; + } + + hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&engine); + ISpObjectToken_Release(token); + if (FAILED(hr)) + return hr; + + EnterCriticalSection(&This->cs); + + if (This->engine) + ISpTTSEngine_Release(This->engine); + This->engine = engine; + + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) { - FIXME("(%p, %p): stub.\n", iface, token); + struct speech_voice *This = impl_from_ISpVoice(iface); + ISpObjectWithToken *engine_token_iface; + HRESULT hr; + + TRACE("(%p, %p).\n", iface, token); - return token_create(NULL, &IID_ISpObjectToken, (void **)token); + if (!token) + return E_POINTER; + + EnterCriticalSection(&This->cs); + + if (!This->engine) + { + LeaveCriticalSection(&This->cs); + return create_default_token(SPCAT_VOICES, token); + } + + if (SUCCEEDED(hr = ISpTTSEngine_QueryInterface(This->engine, &IID_ISpObjectWithToken, (void **)&engine_token_iface))) + { + hr = ISpObjectWithToken_GetObjectToken(engine_token_iface, token); + ISpObjectWithToken_Release(engine_token_iface); + } + + LeaveCriticalSection(&This->cs); + + return hr; } static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *number) @@ -845,6 +929,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->ref = 1; This->output = NULL; + This->engine = NULL; InitializeCriticalSection(&This->cs); From c3dc805c065c5b5c55a7d7a4184061e06a8708df Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 21 Jun 2023 01:58:58 -0400 Subject: [PATCH 640/758] sapi: Implement ISpVoice::Set/GetRate. (cherry picked from commit 5793c57cbc8ff1e2d58af7d495272244bf094ff2) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 30 ++++++++++++++++++++++++++++++ dlls/sapi/tts.c | 26 ++++++++++++++++++++------ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 5141bc27b8c..8564d049949 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -100,6 +100,7 @@ static void test_spvoice(void) ISpObjectTokenCategory *token_cat; ISpObjectToken *token; WCHAR *token_id = NULL, *default_token_id = NULL; + LONG rate; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -149,6 +150,35 @@ static void test_spvoice(void) ISpObjectTokenCategory_Release(token_cat); } + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == 0, "rate = %ld\n", rate); + + hr = ISpVoice_SetRate(voice, 1); + ok(hr == S_OK, "got %#lx.\n", hr); + + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == 1, "rate = %ld\n", rate); + + hr = ISpVoice_SetRate(voice, -1000); + ok(hr == S_OK, "got %#lx.\n", hr); + + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == -1000, "rate = %ld\n", rate); + + hr = ISpVoice_SetRate(voice, 1000); + ok(hr == S_OK, "got %#lx.\n", hr); + + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == 1000, "rate = %ld\n", rate); + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index b7af24790e0..32d3aa31335 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; + LONG rate; CRITICAL_SECTION cs; }; @@ -750,18 +751,30 @@ static HRESULT WINAPI spvoice_GetAlertBoundary(ISpVoice *iface, SPEVENTENUM *bou return E_NOTIMPL; } -static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG adjust) +static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG rate) { - FIXME("(%p, %ld): stub.\n", iface, adjust); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %ld).\n", iface, rate); + + EnterCriticalSection(&This->cs); + This->rate = rate; + LeaveCriticalSection(&This->cs); + + return S_OK; } -static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *adjust) +static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *rate) { - FIXME("(%p, %p): stub.\n", iface, adjust); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, rate); + + EnterCriticalSection(&This->cs); + *rate = This->rate; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume) @@ -930,6 +943,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; + This->rate = 0; InitializeCriticalSection(&This->cs); From 37aeddbe8b589f74bfdd249cce16ff662a4d3d6d Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 21 Jun 2023 02:11:02 -0400 Subject: [PATCH 641/758] sapi: Implement ISpVoice::Set/GetVolume. (cherry picked from commit 02081ed6ff142628c556a8d17f3caa355e0b821f) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 25 +++++++++++++++++++++++++ dlls/sapi/tts.c | 25 +++++++++++++++++++++---- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 8564d049949..534e5842f39 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -101,6 +101,7 @@ static void test_spvoice(void) ISpObjectToken *token; WCHAR *token_id = NULL, *default_token_id = NULL; LONG rate; + USHORT volume; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -179,6 +180,30 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(rate == 1000, "rate = %ld\n", rate); + volume = 0xbeef; + hr = ISpVoice_GetVolume(voice, &volume); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume == 100, "volume = %d\n", volume); + + hr = ISpVoice_SetVolume(voice, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + volume = 0xbeef; + hr = ISpVoice_GetVolume(voice, &volume); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume == 0, "volume = %d\n", volume); + + hr = ISpVoice_SetVolume(voice, 100); + ok(hr == S_OK, "got %#lx.\n", hr); + + volume = 0xbeef; + hr = ISpVoice_GetVolume(voice, &volume); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume == 100, "volume = %d\n", volume); + + hr = ISpVoice_SetVolume(voice, 101); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 32d3aa31335..147734cc9a9 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; + USHORT volume; LONG rate; CRITICAL_SECTION cs; }; @@ -779,16 +780,31 @@ static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *rate) static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume) { - FIXME("(%p, %d): stub.\n", iface, volume); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %d).\n", iface, volume); + + if (volume > 100) + return E_INVALIDARG; + + EnterCriticalSection(&This->cs); + This->volume = volume; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetVolume(ISpVoice *iface, USHORT *volume) { - FIXME("(%p, %p): stub.\n", iface, volume); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, volume); + + EnterCriticalSection(&This->cs); + *volume = This->volume; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_WaitUntilDone(ISpVoice *iface, ULONG timeout) @@ -943,6 +959,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; + This->volume = 100; This->rate = 0; InitializeCriticalSection(&This->cs); From 7de7252a7c71d8aa1bfa02f0f5108a1ad3469f64 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:15:24 -0400 Subject: [PATCH 642/758] sapi: Invoke AddRef in mmaudio SetObjectToken. (cherry picked from commit 5fccc408c66717166b42f6ec5dd508b73193b0aa) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/mmaudio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index d99bcb13672..7452d9a7257 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -293,6 +293,7 @@ static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISp if (This->token) return SPERR_ALREADY_INITIALIZED; + ISpObjectToken_AddRef(token); This->token = token; return S_OK; } From ca043a7621942fc600535f1add4fc13fb1f7d763 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:20:13 -0400 Subject: [PATCH 643/758] sapi: Invoke AddRef in ISpVoice::SetVoice. (cherry picked from commit 9941a716493a4049ef85c691b851513be10dd99d) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 147734cc9a9..b0b3bbd2eff 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -641,7 +641,6 @@ static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) ISpTTSEngine *engine; HRESULT hr; - TRACE("(%p, %p).\n", iface, token); if (!token) @@ -649,6 +648,8 @@ static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) if (FAILED(hr = create_default_token(SPCAT_VOICES, &token))) return hr; } + else + ISpObjectToken_AddRef(token); hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&engine); ISpObjectToken_Release(token); From 7819a21130e8cc9ee4b1ab5f1e5293fab0d8afc9 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 14 Jul 2023 14:10:22 -0400 Subject: [PATCH 644/758] sapi/tests: Fix intermittent duration test failure in mmaudio. (cherry picked from commit bc479f2a19d3f094a7002c74302f200a9c5bb993) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/mmaudio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/mmaudio.c b/dlls/sapi/tests/mmaudio.c index 7c72fb112bf..a990430d784 100644 --- a/dlls/sapi/tests/mmaudio.c +++ b/dlls/sapi/tests/mmaudio.c @@ -167,7 +167,7 @@ static void test_audio_out(void) UINT devid; char *buf = NULL; ULONG written; - DWORD start, end; + DWORD start, duration; HANDLE event = NULL; HRESULT hr; @@ -256,6 +256,7 @@ static void test_audio_out(void) ok(hr == S_OK, "got %#lx.\n", hr); written = 0xdeadbeef; + start = GetTickCount(); hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec * 200 / 1000, &written); ok(hr == S_OK, "got %#lx.\n", hr); ok(written == wfx->nAvgBytesPerSec * 200 / 1000, "got %lu.\n", written); @@ -263,7 +264,8 @@ static void test_audio_out(void) hr = ISpMMSysAudio_Write(mmaudio, buf, wfx->nAvgBytesPerSec * 200 / 1000, NULL); ok(hr == S_OK, "got %#lx.\n", hr); - start = GetTickCount(); + hr = ISpMMSysAudio_Commit(mmaudio, STGC_DEFAULT); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); event = ISpMMSysAudio_EventHandle(mmaudio); ok(event != NULL, "event == NULL.\n"); @@ -271,8 +273,8 @@ static void test_audio_out(void) hr = WaitForSingleObject(event, 1000); ok(hr == WAIT_OBJECT_0, "got %#lx.\n", hr); - end = GetTickCount(); - ok(end - start <= 500, "waited for %lu ms.\n", end - start); + duration = GetTickCount() - start; + ok(duration > 200 && duration < 800, "took %lu ms.\n", duration); hr = ISpMMSysAudio_SetState(mmaudio, SPAS_CLOSED, 0); ok(hr == S_OK, "got %#lx.\n", hr); From 4d60d42e11bc59d90914a080c469c224065bbbb7 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 14 Jul 2023 14:17:26 -0400 Subject: [PATCH 645/758] sapi/tests: Fix ISpObjectToken::CreateInstance E_ACCESSDENIED error. (cherry picked from commit 327667a620b1d0c9f8dd47b9a85343a7badd4b20) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/token.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/tests/token.c b/dlls/sapi/tests/token.c index 5a3bc0e340e..8befd98c2a5 100644 --- a/dlls/sapi/tests/token.c +++ b/dlls/sapi/tests/token.c @@ -601,7 +601,7 @@ static IClassFactory test_class_cf = { &ClassFactoryVtbl }; static void test_object_token(void) { - static const WCHAR test_token_id[] = L"HKEY_CURRENT_USER\\Software\\Winetest\\sapi\\TestToken"; + static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Wine\\Winetest\\sapi\\TestToken"; ISpObjectToken *token; ISpDataKey *sub_key; @@ -742,7 +742,12 @@ static void test_object_token(void) ok( hr == S_OK, "got %08lx\n", hr ); hr = ISpObjectToken_SetId( token, NULL, test_token_id, TRUE ); - ok( hr == S_OK, "got %08lx\n", hr ); + ok( hr == S_OK || broken(hr == E_ACCESSDENIED) /* win1064_adm */, "got %08lx\n", hr ); + if (hr == E_ACCESSDENIED) { + win_skip( "token SetId access denied\n" ); + ISpObjectToken_Release( token ); + return; + } hr = ISpObjectToken_CreateKey( token, L"Attributes", &sub_key ); ok( hr == S_OK, "got %08lx\n", hr ); @@ -763,10 +768,6 @@ static void test_object_token(void) test_class_token = NULL; hr = ISpObjectToken_CreateInstance( token, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&obj ); - if ( hr == E_ACCESSDENIED ) { - win_skip( "ISpObjectToken_CreateInstance returned E_ACCESSDENIED\n" ); - return; - } ok( hr == S_OK, "got %08lx\n", hr ); ok( test_class_token != NULL, "test_class_token not set\n" ); From ad59756a85c674c9b967a596d673ac6ebad10d48 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 28 Jun 2023 02:10:48 -0400 Subject: [PATCH 646/758] include: Fix ISpTTSEngineSite::GetActions return type. (cherry picked from commit 5f3dee9702f965c1906f7b466fd18451a0a1a87b) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- include/sapiddk.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sapiddk.idl b/include/sapiddk.idl index 46aebbb9a12..8f9abf4e117 100644 --- a/include/sapiddk.idl +++ b/include/sapiddk.idl @@ -72,7 +72,7 @@ typedef enum SPVESACTIONS ] interface ISpTTSEngineSite : ISpEventSink { - HRESULT GetActions(); + DWORD GetActions(); HRESULT Write([in] const void *pBuff, [in] ULONG cb, [out] ULONG *pcbWritten); From 013841e23032935ac1021e2394de3db39676a7a4 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 12:46:04 -0400 Subject: [PATCH 647/758] sapi: Handle queue not initialized in async_empty_queue. (cherry picked from commit 4bfd0a2370a24c2038e03454ca6511534aac0d20) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/async.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index 57ae89ad723..e09c6c64f9a 100644 --- a/dlls/sapi/async.c +++ b/dlls/sapi/async.c @@ -52,6 +52,8 @@ void async_empty_queue(struct async_queue *queue) { struct async_task *task, *next; + if (!queue->init) return; + EnterCriticalSection(&queue->cs); LIST_FOR_EACH_ENTRY_SAFE(task, next, &queue->tasks, struct async_task, entry) { From b3777cfaad4f9a5e2b1c9b210c93b6e0d03739a5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 12:47:00 -0400 Subject: [PATCH 648/758] sapi: Reset empty event after queuing a task in async_queue_task. (cherry picked from commit 9d407a111781548db23d1a38ca61fd6b8f50fdf8) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/async.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index e09c6c64f9a..491ca657c1a 100644 --- a/dlls/sapi/async.c +++ b/dlls/sapi/async.c @@ -165,6 +165,7 @@ HRESULT async_queue_task(struct async_queue *queue, struct async_task *task) list_add_tail(&queue->tasks, &task->entry); LeaveCriticalSection(&queue->cs); + ResetEvent(queue->empty); SetEvent(queue->wait); return S_OK; From de9a60f27355a2273cb92231932590e63e955ccd Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 12:57:49 -0400 Subject: [PATCH 649/758] sapi: Implement ISpVoice::Speak SPF_PURGEBEFORESPEAK. Also introduce an async task queue. (cherry picked from commit f9dff8a6fa759cd1a2d9f32dcafe9588e80a5469) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 9 +++++++++ dlls/sapi/tts.c | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 534e5842f39..586869aa33e 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -102,6 +102,7 @@ static void test_spvoice(void) WCHAR *token_id = NULL, *default_token_id = NULL; LONG rate; USHORT volume; + ULONG stream_num; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -204,6 +205,14 @@ static void test_spvoice(void) hr = ISpVoice_SetVolume(voice, 101); ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + stream_num = 0xdeadbeef; + hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, &stream_num); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(stream_num == 0xdeadbeef, "got %lu.\n", stream_num); + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index b0b3bbd2eff..98d74a11f2d 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -45,6 +45,7 @@ struct speech_voice ISpTTSEngine *engine; USHORT volume; LONG rate; + struct async_queue queue; CRITICAL_SECTION cs; }; @@ -143,6 +144,7 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) if (!ref) { + async_cancel_queue(&This->queue); if (This->output) ISpStreamFormat_Release(This->output); if (This->engine) ISpTTSEngine_Release(This->engine); DeleteCriticalSection(&This->cs); @@ -697,9 +699,39 @@ static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) return hr; } -static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *number) +static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *stream_num_out) { - FIXME("(%p, %p, %#lx, %p): stub.\n", iface, contents, flags, number); + struct speech_voice *This = impl_from_ISpVoice(iface); + + FIXME("(%p, %p, %#lx, %p): semi-stub.\n", iface, contents, flags, stream_num_out); + + if (flags & ~SPF_PURGEBEFORESPEAK) + { + FIXME("flags %#lx not implemented.\n", flags & ~SPF_PURGEBEFORESPEAK); + return E_NOTIMPL; + } + + if (flags & SPF_PURGEBEFORESPEAK) + { + ISpAudio *audio; + + EnterCriticalSection(&This->cs); + + if (This->output && SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio))) + { + ISpAudio_SetState(audio, SPAS_CLOSED, 0); + ISpAudio_Release(audio); + } + + LeaveCriticalSection(&This->cs); + + async_empty_queue(&This->queue); + + if (!contents || !*contents) + return S_OK; + } + else if (!contents) + return E_POINTER; return E_NOTIMPL; } @@ -962,6 +994,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->engine = NULL; This->volume = 100; This->rate = 0; + memset(&This->queue, 0, sizeof(This->queue)); InitializeCriticalSection(&This->cs); From ee10a51aaee669c2d30385a2591bee23a48a5f00 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:49:09 -0400 Subject: [PATCH 650/758] sapi: Partially implement ISpVoice::Speak SPF_ASYNC. (cherry picked from commit a234bbc748a0e02b00f059e739dc3ca8135264a0) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tts.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 98d74a11f2d..bc4a225a3f5 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -699,15 +699,34 @@ static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) return hr; } +struct speak_task +{ + struct async_task task; + + struct speech_voice *voice; + SPVTEXTFRAG *frag_list; + DWORD flags; +}; + +static void speak_proc(struct async_task *task) +{ + FIXME("(%p): stub.\n", task); +} + static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *stream_num_out) { struct speech_voice *This = impl_from_ISpVoice(iface); + SPVTEXTFRAG *frag; + struct speak_task *speak_task = NULL; + size_t contents_len, contents_size; + HRESULT hr; FIXME("(%p, %p, %#lx, %p): semi-stub.\n", iface, contents, flags, stream_num_out); - if (flags & ~SPF_PURGEBEFORESPEAK) + flags &= ~SPF_IS_NOT_XML; + if (flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC)) { - FIXME("flags %#lx not implemented.\n", flags & ~SPF_PURGEBEFORESPEAK); + FIXME("flags %#lx not implemented.\n", flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC)); return E_NOTIMPL; } @@ -733,7 +752,57 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR else if (!contents) return E_POINTER; - return E_NOTIMPL; + if (!(flags & SPF_ASYNC)) + { + FIXME("Synchronous Speak not implemented.\n"); + return E_NOTIMPL; + } + + contents_len = wcslen(contents); + contents_size = sizeof(WCHAR) * (contents_len + 1); + + if (!This->output) + { + /* Create a new output stream with the default output. */ + if (FAILED(hr = ISpVoice_SetOutput(iface, NULL, TRUE))) + return hr; + } + + if (!This->engine) + { + /* Create a new engine with the default voice. */ + if (FAILED(hr = ISpVoice_SetVoice(iface, NULL))) + return hr; + } + + if (!(frag = heap_alloc(sizeof(*frag) + contents_size))) + return E_OUTOFMEMORY; + memset(frag, 0, sizeof(*frag)); + memcpy(frag + 1, contents, contents_size); + frag->State.eAction = SPVA_Speak; + frag->State.Volume = 100; + frag->pTextStart = (WCHAR *)(frag + 1); + frag->ulTextLen = contents_len; + frag->ulTextSrcOffset = 0; + speak_task = heap_alloc(sizeof(*speak_task)); + + speak_task->task.proc = speak_proc; + speak_task->voice = This; + speak_task->frag_list = frag; + speak_task->flags = flags & SPF_NLP_SPEAK_PUNC; + + if (FAILED(hr = async_queue_task(&This->queue, (struct async_task *)speak_task))) + { + WARN("Failed to queue task: %#lx.\n", hr); + goto fail; + } + + if (flags & SPF_ASYNC) + return S_OK; +fail: + heap_free(frag); + heap_free(speak_task); + return hr; } static HRESULT WINAPI spvoice_SpeakStream(ISpVoice *iface, IStream *stream, DWORD flags, ULONG *number) From fc1c4e051b7d177c68cb6ed105295a804de45cc9 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:17:43 -0400 Subject: [PATCH 651/758] sapi: Implement synchronous ISpVoice::Speak. (cherry picked from commit 86a31446830c8dcadc558b6793b0c339d709d709) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tts.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index bc4a225a3f5..fb2fc9b9991 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -699,9 +699,16 @@ static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) return hr; } +struct async_result +{ + HANDLE done; + HRESULT hr; +}; + struct speak_task { struct async_task task; + struct async_result *result; struct speech_voice *voice; SPVTEXTFRAG *frag_list; @@ -710,7 +717,15 @@ struct speak_task static void speak_proc(struct async_task *task) { + struct speak_task *speak_task = (struct speak_task *)task; + FIXME("(%p): stub.\n", task); + + if (speak_task->result) + { + speak_task->result->hr = E_NOTIMPL; + SetEvent(speak_task->result->done); + } } static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *stream_num_out) @@ -718,6 +733,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR struct speech_voice *This = impl_from_ISpVoice(iface); SPVTEXTFRAG *frag; struct speak_task *speak_task = NULL; + struct async_result *result = NULL; size_t contents_len, contents_size; HRESULT hr; @@ -752,12 +768,6 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR else if (!contents) return E_POINTER; - if (!(flags & SPF_ASYNC)) - { - FIXME("Synchronous Speak not implemented.\n"); - return E_NOTIMPL; - } - contents_len = wcslen(contents); contents_size = sizeof(WCHAR) * (contents_len + 1); @@ -787,10 +797,23 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR speak_task = heap_alloc(sizeof(*speak_task)); speak_task->task.proc = speak_proc; + speak_task->result = NULL; speak_task->voice = This; speak_task->frag_list = frag; speak_task->flags = flags & SPF_NLP_SPEAK_PUNC; + if (!(flags & SPF_ASYNC)) + { + if (!(result = heap_alloc(sizeof(*result)))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + result->hr = E_FAIL; + result->done = CreateEventW(NULL, FALSE, FALSE, NULL); + speak_task->result = result; + } + if (FAILED(hr = async_queue_task(&This->queue, (struct async_task *)speak_task))) { WARN("Failed to queue task: %#lx.\n", hr); @@ -799,9 +822,23 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR if (flags & SPF_ASYNC) return S_OK; + else + { + WaitForSingleObject(result->done, INFINITE); + hr = result->hr; + CloseHandle(result->done); + heap_free(result); + return hr; + } + fail: heap_free(frag); heap_free(speak_task); + if (result) + { + CloseHandle(result->done); + heap_free(result); + } return hr; } From 39071a8f8c35d15f15305e1df7747b85526babfc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:19:18 -0400 Subject: [PATCH 652/758] sapi: Introduce ISpTTSEngineSite stub. (cherry picked from commit 68660958f63dce365125e0b4d50479d7788e43e2) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 5 ++ dlls/sapi/tts.c | 173 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 586869aa33e..ffe2e5f73a7 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -38,6 +38,7 @@ static void test_interfaces(void) { ISpeechVoice *speech_voice, *speech_voice2; IConnectionPointContainer *container; + ISpTTSEngineSite *site; ISpVoice *spvoice, *spvoice2; IDispatch *dispatch; IUnknown *unk; @@ -90,6 +91,10 @@ static void test_interfaces(void) EXPECT_REF(container, 2); IConnectionPointContainer_Release(container); + hr = ISpeechVoice_QueryInterface(speech_voice, &IID_ISpTTSEngineSite, + (void **)&site); + ok(hr == E_NOINTERFACE, "ISpeechVoice_QueryInterface for ISpTTSEngineSite returned: %#lx.\n", hr); + ISpeechVoice_Release(speech_voice); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index fb2fc9b9991..7ec5aa059d9 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; + LONG cur_stream_num; USHORT volume; LONG rate; struct async_queue queue; @@ -64,6 +65,20 @@ static inline struct speech_voice *impl_from_IConnectionPointContainer(IConnecti return CONTAINING_RECORD(iface, struct speech_voice, IConnectionPointContainer_iface); } +struct tts_engine_site +{ + ISpTTSEngineSite ISpTTSEngineSite_iface; + LONG ref; + + struct speech_voice *voice; + ULONG stream_num; +}; + +static inline struct tts_engine_site *impl_from_ISpTTSEngineSite(ISpTTSEngineSite *iface) +{ + return CONTAINING_RECORD(iface, struct tts_engine_site, ISpTTSEngineSite_iface); +} + static HRESULT create_default_token(const WCHAR *cat_id, ISpObjectToken **token) { ISpObjectTokenCategory *cat; @@ -712,6 +727,7 @@ struct speak_task struct speech_voice *voice; SPVTEXTFRAG *frag_list; + ISpTTSEngineSite *site; DWORD flags; }; @@ -728,13 +744,17 @@ static void speak_proc(struct async_task *task) } } +static HRESULT ttsenginesite_create(struct speech_voice *voice, ULONG stream_num, ISpTTSEngineSite **site); + static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *stream_num_out) { struct speech_voice *This = impl_from_ISpVoice(iface); + ISpTTSEngineSite *site = NULL; SPVTEXTFRAG *frag; struct speak_task *speak_task = NULL; struct async_result *result = NULL; size_t contents_len, contents_size; + ULONG stream_num; HRESULT hr; FIXME("(%p, %p, %#lx, %p): semi-stub.\n", iface, contents, flags, stream_num_out); @@ -794,12 +814,21 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR frag->pTextStart = (WCHAR *)(frag + 1); frag->ulTextLen = contents_len; frag->ulTextSrcOffset = 0; + + stream_num = InterlockedIncrement(&This->cur_stream_num); + if (FAILED(hr = ttsenginesite_create(This, stream_num, &site))) + { + FIXME("Failed to create ttsenginesite: %#lx.\n", hr); + goto fail; + } + speak_task = heap_alloc(sizeof(*speak_task)); speak_task->task.proc = speak_proc; speak_task->result = NULL; speak_task->voice = This; speak_task->frag_list = frag; + speak_task->site = site; speak_task->flags = flags & SPF_NLP_SPEAK_PUNC; if (!(flags & SPF_ASYNC)) @@ -820,6 +849,9 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR goto fail; } + if (stream_num_out) + *stream_num_out = stream_num; + if (flags & SPF_ASYNC) return S_OK; else @@ -832,6 +864,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR } fail: + if (site) ISpTTSEngineSite_Release(site); heap_free(frag); heap_free(speak_task); if (result) @@ -1032,6 +1065,145 @@ static const ISpVoiceVtbl spvoice_vtbl = spvoice_DisplayUI }; +/* ISpTTSEngineSite interface */ +static HRESULT WINAPI ttsenginesite_QueryInterface(ISpTTSEngineSite *iface, REFIID iid, void **obj) +{ + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); + + TRACE("(%p, %s %p).\n", iface, debugstr_guid(iid), obj); + + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_ISpTTSEngineSite)) + *obj = &This->ISpTTSEngineSite_iface; + else + { + *obj = NULL; + FIXME("interface %s not implemented.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static ULONG WINAPI ttsenginesite_AddRef(ISpTTSEngineSite *iface) +{ + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p): ref=%lu.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI ttsenginesite_Release(ISpTTSEngineSite *iface) +{ + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); + + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p): ref=%lu.\n", iface, ref); + + if (!ref) + { + if (This->voice) + ISpeechVoice_Release(&This->voice->ISpeechVoice_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI ttsenginesite_AddEvents(ISpTTSEngineSite *iface, const SPEVENT *events, ULONG count) +{ + FIXME("(%p, %p, %ld): stub.\n", iface, events, count); + + return S_OK; +} + +static HRESULT WINAPI ttsenginesite_GetEventInterest(ISpTTSEngineSite *iface, ULONGLONG *interest) +{ + FIXME("(%p, %p): stub.\n", iface, interest); + + return E_NOTIMPL; +} + +static DWORD WINAPI ttsenginesite_GetActions(ISpTTSEngineSite *iface) +{ + FIXME("(%p): stub.\n", iface); + + return SPVES_CONTINUE; +} + +static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *buf, ULONG cb, ULONG *cb_written) +{ + FIXME("(%p, %p, %ld, %p): stub.\n", iface, buf, cb, cb_written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ttsenginesite_GetRate(ISpTTSEngineSite *iface, LONG *rate) +{ + FIXME("(%p, %p): stub.\n", iface, rate); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ttsenginesite_GetVolume(ISpTTSEngineSite *iface, USHORT *volume) +{ + FIXME("(%p, %p): stub.\n", iface, volume); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ttsenginesite_GetSkipInfo(ISpTTSEngineSite *iface, SPVSKIPTYPE *type, LONG *skip_count) +{ + FIXME("(%p, %p, %p): stub.\n", iface, type, skip_count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ttsenginesite_CompleteSkip(ISpTTSEngineSite *iface, LONG num_skipped) +{ + FIXME("(%p, %ld): stub.\n", iface, num_skipped); + + return E_NOTIMPL; +} + +const static ISpTTSEngineSiteVtbl ttsenginesite_vtbl = +{ + ttsenginesite_QueryInterface, + ttsenginesite_AddRef, + ttsenginesite_Release, + ttsenginesite_AddEvents, + ttsenginesite_GetEventInterest, + ttsenginesite_GetActions, + ttsenginesite_Write, + ttsenginesite_GetRate, + ttsenginesite_GetVolume, + ttsenginesite_GetSkipInfo, + ttsenginesite_CompleteSkip +}; + +static HRESULT ttsenginesite_create(struct speech_voice *voice, ULONG stream_num, ISpTTSEngineSite **site) +{ + struct tts_engine_site *This = heap_alloc(sizeof(*This)); + + if (!This) return E_OUTOFMEMORY; + + This->ISpTTSEngineSite_iface.lpVtbl = &ttsenginesite_vtbl; + + This->ref = 1; + This->voice = voice; + This->stream_num = stream_num; + + ISpeechVoice_AddRef(&This->voice->ISpeechVoice_iface); + + *site = &This->ISpTTSEngineSite_iface; + + return S_OK; +} + /* IConnectionPointContainer interface */ static HRESULT WINAPI container_QueryInterface(IConnectionPointContainer *iface, REFIID iid, void **obj) { @@ -1098,6 +1270,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; + This->cur_stream_num = 0; This->volume = 100; This->rate = 0; memset(&This->queue, 0, sizeof(This->queue)); From da69c1d32870f497638bb08db9a50e329d94e1c9 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:28:21 -0400 Subject: [PATCH 653/758] sapi: Implement ISpVoice::Speak speak_proc. (cherry picked from commit 303bdc2e4bd9f6b0b1056a3607abc236aa63bbf0) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 314 ++++++++++++++++++++++++++++++++++++++++++ dlls/sapi/tts.c | 80 ++++++++++- 2 files changed, 391 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index ffe2e5f73a7..c17e4f5b7c3 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -98,16 +98,266 @@ static void test_interfaces(void) ISpeechVoice_Release(speech_voice); } +#define TESTENGINE_CLSID L"{57C7E6B1-2FC2-4E8E-B968-1410A39E7198}" +static const GUID CLSID_TestEngine = {0x57C7E6B1,0x2FC2,0x4E8E,{0xB9,0x68,0x14,0x10,0xA3,0x9E,0x71,0x98}}; + +struct test_engine +{ + ISpTTSEngine ISpTTSEngine_iface; + ISpObjectWithToken ISpObjectWithToken_iface; + + ISpObjectToken *token; + + BOOL speak_called; + DWORD flags; + GUID fmtid; + SPVTEXTFRAG *frag_list; +}; + +static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frag_list) +{ + SPVTEXTFRAG *frag, *prev = NULL; + + if (!frag_list) + { + *ret_frag_list = NULL; + return; + } + + while (frag_list) + { + frag = malloc(sizeof(*frag) + frag_list->ulTextLen * sizeof(WCHAR)); + memcpy(frag, frag_list, sizeof(*frag)); + + if (frag_list->pTextStart) + { + frag->pTextStart = (WCHAR *)(frag + 1); + memcpy(frag + 1, frag_list->pTextStart, frag->ulTextLen * sizeof(WCHAR)); + } + + frag->pNext = NULL; + + if (prev) + prev->pNext = frag; + else + *ret_frag_list = frag; + + prev = frag; + frag_list = frag_list->pNext; + } +} + +static void reset_engine_params(struct test_engine *engine) +{ + SPVTEXTFRAG *frag, *next; + + engine->speak_called = FALSE; + engine->flags = 0xdeadbeef; + memset(&engine->fmtid, 0xde, sizeof(engine->fmtid)); + + for (frag = engine->frag_list; frag; frag = next) + { + next = frag->pNext; + free(frag); + } + engine->frag_list = NULL; +} + +static inline struct test_engine *impl_from_ISpTTSEngine(ISpTTSEngine *iface) +{ + return CONTAINING_RECORD(iface, struct test_engine, ISpTTSEngine_iface); +} + +static inline struct test_engine *impl_from_ISpObjectWithToken(ISpObjectWithToken *iface) +{ + return CONTAINING_RECORD(iface, struct test_engine, ISpObjectWithToken_iface); +} + +static HRESULT WINAPI test_engine_QueryInterface(ISpTTSEngine *iface, REFIID iid, void **obj) +{ + struct test_engine *engine = impl_from_ISpTTSEngine(iface); + + if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ISpTTSEngine)) + *obj = &engine->ISpTTSEngine_iface; + else if (IsEqualIID(iid, &IID_ISpObjectWithToken)) + *obj = &engine->ISpObjectWithToken_iface; + else + { + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + return S_OK; +} + +static ULONG WINAPI test_engine_AddRef(ISpTTSEngine *iface) +{ + return 2; +} + +static ULONG WINAPI test_engine_Release(ISpTTSEngine *iface) +{ + return 1; +} + +static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID fmtid, + const WAVEFORMATEX *wfx, const SPVTEXTFRAG *frag_list, + ISpTTSEngineSite *site) +{ + struct test_engine *engine = impl_from_ISpTTSEngine(iface); + + engine->flags = flags; + engine->fmtid = *fmtid; + copy_frag_list(frag_list, &engine->frag_list); + engine->speak_called = TRUE; + + return S_OK; +} + +static HRESULT WINAPI test_engine_GetOutputFormat(ISpTTSEngine *iface, const GUID *fmtid, + const WAVEFORMATEX *wfx, GUID *out_fmtid, + WAVEFORMATEX **out_wfx) +{ + *out_fmtid = SPDFID_WaveFormatEx; + *out_wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX)); + (*out_wfx)->wFormatTag = WAVE_FORMAT_PCM; + (*out_wfx)->nChannels = 1; + (*out_wfx)->nSamplesPerSec = 22050; + (*out_wfx)->wBitsPerSample = 16; + (*out_wfx)->nBlockAlign = 2; + (*out_wfx)->nAvgBytesPerSec = 22050 * 2; + (*out_wfx)->cbSize = 0; + + return S_OK; +} + +static ISpTTSEngineVtbl test_engine_vtbl = +{ + test_engine_QueryInterface, + test_engine_AddRef, + test_engine_Release, + test_engine_Speak, + test_engine_GetOutputFormat, +}; + +static HRESULT WINAPI objwithtoken_QueryInterface(ISpObjectWithToken *iface, REFIID iid, void **obj) +{ + struct test_engine *engine = impl_from_ISpObjectWithToken(iface); + + return ISpTTSEngine_QueryInterface(&engine->ISpTTSEngine_iface, iid, obj); +} + +static ULONG WINAPI objwithtoken_AddRef(ISpObjectWithToken *iface) +{ + return 2; +} + +static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface) +{ + return 1; +} + +static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token) +{ + struct test_engine *engine = impl_from_ISpObjectWithToken(iface); + + if (!token) + return E_INVALIDARG; + + ISpObjectToken_AddRef(token); + engine->token = token; + + return S_OK; +} + +static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token) +{ + struct test_engine *engine = impl_from_ISpObjectWithToken(iface); + + *token = engine->token; + if (*token) + ISpObjectToken_AddRef(*token); + + return S_OK; +} + +static const ISpObjectWithTokenVtbl objwithtoken_vtbl = +{ + objwithtoken_QueryInterface, + objwithtoken_AddRef, + objwithtoken_Release, + objwithtoken_SetObjectToken, + objwithtoken_GetObjectToken +}; + +static struct test_engine test_engine = {{&test_engine_vtbl}, {&objwithtoken_vtbl}}; + +static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) +{ + if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IClassFactory, riid)) + { + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, + IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + ok(pUnkOuter == NULL, "pUnkOuter != NULL.\n"); + ok(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISpTTSEngine), + "riid = %s.\n", wine_dbgstr_guid(riid)); + + *ppv = &test_engine.ISpTTSEngine_iface; + return S_OK; +} + +static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock) +{ + ok(0, "unexpected call.\n"); + return E_NOTIMPL; +} + +static const IClassFactoryVtbl ClassFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + ClassFactory_CreateInstance, + ClassFactory_LockServer +}; + +static IClassFactory test_engine_cf = { &ClassFactoryVtbl }; + static void test_spvoice(void) { + static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Wine\\Winetest\\sapi\\tts\\TestEngine"; + static const WCHAR test_text[] = L"Hello! This is a test sentence."; + ISpVoice *voice; ISpMMSysAudio *audio_out; ISpObjectTokenCategory *token_cat; ISpObjectToken *token; WCHAR *token_id = NULL, *default_token_id = NULL; + ISpDataKey *attrs_key; LONG rate; USHORT volume; ULONG stream_num; + DWORD regid; + DWORD start, duration; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -210,15 +460,79 @@ static void test_spvoice(void) hr = ISpVoice_SetVolume(voice, 101); ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + hr = CoRegisterClassObject(&CLSID_TestEngine, (IUnknown *)&test_engine_cf, + CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, ®id); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&token); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpObjectToken_SetId(token, NULL, test_token_id, TRUE); + ok(hr == S_OK || broken(hr == E_ACCESSDENIED) /* w1064_adm */, "got %#lx.\n", hr); + if (hr == E_ACCESSDENIED) + { + win_skip("token SetId access denied.\n"); + goto done; + } + + ISpObjectToken_SetStringValue(token, L"CLSID", TESTENGINE_CLSID); + hr = ISpObjectToken_CreateKey(token, L"Attributes", &attrs_key); + ok(hr == S_OK, "got %#lx.\n", hr); + ISpDataKey_SetStringValue(attrs_key, L"Language", L"409"); + ISpDataKey_Release(attrs_key); + + hr = ISpVoice_SetVoice(voice, token); + ok(hr == S_OK, "got %#lx.\n", hr); + + test_engine.speak_called = FALSE; hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL); ok(hr == S_OK, "got %#lx.\n", hr); + ok(!test_engine.speak_called, "ISpTTSEngine::Speak was called.\n"); stream_num = 0xdeadbeef; hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, &stream_num); ok(hr == S_OK, "got %#lx.\n", hr); ok(stream_num == 0xdeadbeef, "got %lu.\n", stream_num); + test_engine.speak_called = FALSE; + stream_num = 0xdeadbeef; + start = GetTickCount(); + hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, &stream_num); + duration = GetTickCount() - start; + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); + ok(test_engine.flags == SPF_DEFAULT, "got %#lx.\n", test_engine.flags); + ok(test_engine.frag_list != NULL, "frag_list is NULL.\n"); + ok(test_engine.frag_list->pNext == NULL, "frag_list->pNext != NULL.\n"); + ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); + ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), + "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(stream_num == 1, "got %lu.\n", stream_num); + ok(duration < 500, "took %lu ms.\n", duration); + + reset_engine_params(&test_engine); + stream_num = 0xdeadbeef; + start = GetTickCount(); + hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC | SPF_NLP_SPEAK_PUNC, &stream_num); + duration = GetTickCount() - start; + ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(stream_num == 1, "got %lu.\n", stream_num); + ok(duration < 500, "took %lu ms.\n", duration); + + Sleep(200); + ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); + ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags); + ok(test_engine.frag_list != NULL, "frag_list is NULL.\n"); + ok(test_engine.frag_list->pNext == NULL, "frag_list->pNext != NULL.\n"); + ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); + ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), + "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + +done: + reset_engine_params(&test_engine); ISpVoice_Release(voice); + ISpObjectToken_Release(token); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 7ec5aa059d9..9a022aca73c 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -731,15 +731,89 @@ struct speak_task DWORD flags; }; +static HRESULT set_output_format(ISpStreamFormat *output, ISpTTSEngine *engine, GUID *fmtid, WAVEFORMATEX **wfx) +{ + GUID output_fmtid; + WAVEFORMATEX *output_wfx = NULL; + ISpAudio *audio = NULL; + HRESULT hr; + + if (FAILED(hr = ISpStreamFormat_GetFormat(output, &output_fmtid, &output_wfx))) + return hr; + if (FAILED(hr = ISpTTSEngine_GetOutputFormat(engine, &output_fmtid, output_wfx, fmtid, wfx))) + goto done; + if (!IsEqualGUID(fmtid, &SPDFID_WaveFormatEx)) + { + hr = E_INVALIDARG; + goto done; + } + + if (memcmp(output_wfx, *wfx, sizeof(WAVEFORMATEX)) || + memcmp(output_wfx + 1, *wfx + 1, output_wfx->cbSize)) + { + if (FAILED(hr = ISpStreamFormat_QueryInterface(output, &IID_ISpAudio, (void **)&audio)) || + FAILED(hr = ISpAudio_SetFormat(audio, &SPDFID_WaveFormatEx, *wfx))) + goto done; + } + +done: + CoTaskMemFree(output_wfx); + if (audio) ISpAudio_Release(audio); + return hr; +} + static void speak_proc(struct async_task *task) { struct speak_task *speak_task = (struct speak_task *)task; + struct speech_voice *This = speak_task->voice; + GUID fmtid; + WAVEFORMATEX *wfx = NULL; + ISpTTSEngine *engine = NULL; + ISpAudio *audio = NULL; + HRESULT hr; + + TRACE("(%p).\n", task); - FIXME("(%p): stub.\n", task); + EnterCriticalSection(&This->cs); + + if (FAILED(hr = set_output_format(This->output, This->engine, &fmtid, &wfx))) + { + LeaveCriticalSection(&This->cs); + ERR("failed setting output format: %#lx.\n", hr); + goto done; + } + engine = This->engine; + ISpTTSEngine_AddRef(engine); + + if (SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio))) + ISpAudio_SetState(audio, SPAS_RUN, 0); + + LeaveCriticalSection(&This->cs); + + hr = ISpTTSEngine_Speak(engine, speak_task->flags, &fmtid, wfx, speak_task->frag_list, speak_task->site); + if (SUCCEEDED(hr)) + { + ISpStreamFormat_Commit(This->output, STGC_DEFAULT); + if (audio) + WaitForSingleObject(ISpAudio_EventHandle(audio), INFINITE); + } + else + WARN("ISpTTSEngine_Speak failed: %#lx.\n", hr); + +done: + if (audio) + { + ISpAudio_SetState(audio, SPAS_CLOSED, 0); + ISpAudio_Release(audio); + } + CoTaskMemFree(wfx); + if (engine) ISpTTSEngine_Release(engine); + heap_free(speak_task->frag_list); + ISpTTSEngineSite_Release(speak_task->site); if (speak_task->result) { - speak_task->result->hr = E_NOTIMPL; + speak_task->result->hr = hr; SetEvent(speak_task->result->done); } } @@ -757,7 +831,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR ULONG stream_num; HRESULT hr; - FIXME("(%p, %p, %#lx, %p): semi-stub.\n", iface, contents, flags, stream_num_out); + TRACE("(%p, %p, %#lx, %p).\n", iface, contents, flags, stream_num_out); flags &= ~SPF_IS_NOT_XML; if (flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC)) From 6ff9118dcd6ced9fbd77ddabdfa47eddfb96a27e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:51:16 -0400 Subject: [PATCH 654/758] sapi: Implement ISpTTSEngineSite::Write. (cherry picked from commit 0d09ab378e98b7b5da1ac78439fec09d1ce30994) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 14 +++++++++++++- dlls/sapi/tts.c | 10 ++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index c17e4f5b7c3..d2b98e6fbfb 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -206,12 +206,24 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI ISpTTSEngineSite *site) { struct test_engine *engine = impl_from_ISpTTSEngine(iface); + char *buf; + int i; + HRESULT hr; engine->flags = flags; engine->fmtid = *fmtid; copy_frag_list(frag_list, &engine->frag_list); engine->speak_called = TRUE; + buf = calloc(1, 22050 * 2 / 5); + for (i = 0; i < 5; i++) + { + hr = ISpTTSEngineSite_Write(site, buf, 22050 * 2 / 5, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + Sleep(100); + } + free(buf); + return S_OK; } @@ -509,7 +521,7 @@ static void test_spvoice(void) ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); ok(stream_num == 1, "got %lu.\n", stream_num); - ok(duration < 500, "took %lu ms.\n", duration); + ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); reset_engine_params(&test_engine); stream_num = 0xdeadbeef; diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 9a022aca73c..acbef907646 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -27,6 +27,7 @@ #include "objbase.h" #include "sapiddk.h" +#include "sperror.h" #include "wine/debug.h" @@ -1211,9 +1212,14 @@ static DWORD WINAPI ttsenginesite_GetActions(ISpTTSEngineSite *iface) static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *buf, ULONG cb, ULONG *cb_written) { - FIXME("(%p, %p, %ld, %p): stub.\n", iface, buf, cb, cb_written); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %ld, %p).\n", iface, buf, cb, cb_written); + + if (!This->voice->output) + return SPERR_UNINITIALIZED; + + return ISpStreamFormat_Write(This->voice->output, buf, cb, cb_written); } static HRESULT WINAPI ttsenginesite_GetRate(ISpTTSEngineSite *iface, LONG *rate) From c693b1726fac453697e140127f9fdab24ba137f3 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:51:36 -0400 Subject: [PATCH 655/758] sapi: Implement ISpTTSEngineSite::GetActions/Rate/Volume. (cherry picked from commit ee8c8f6533583f1603a292d0d33d939b815beb22) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 44 +++++++++++++++++++++++++++++++++++-- dlls/sapi/tts.c | 51 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index d2b98e6fbfb..86a9312a37b 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -112,6 +112,8 @@ struct test_engine DWORD flags; GUID fmtid; SPVTEXTFRAG *frag_list; + LONG rate; + USHORT volume; }; static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frag_list) @@ -154,6 +156,8 @@ static void reset_engine_params(struct test_engine *engine) engine->speak_called = FALSE; engine->flags = 0xdeadbeef; memset(&engine->fmtid, 0xde, sizeof(engine->fmtid)); + engine->rate = 0xdeadbeef; + engine->volume = 0xbeef; for (frag = engine->frag_list; frag; frag = next) { @@ -206,6 +210,7 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI ISpTTSEngineSite *site) { struct test_engine *engine = impl_from_ISpTTSEngine(iface); + DWORD actions; char *buf; int i; HRESULT hr; @@ -215,11 +220,26 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI copy_frag_list(frag_list, &engine->frag_list); engine->speak_called = TRUE; + actions = ISpTTSEngineSite_GetActions(site); + ok(actions == (SPVES_CONTINUE | SPVES_RATE | SPVES_VOLUME), "got %#lx.\n", actions); + + hr = ISpTTSEngineSite_GetRate(site, &engine->rate); + ok(hr == S_OK, "got %#lx.\n", hr); + actions = ISpTTSEngineSite_GetActions(site); + ok(actions == (SPVES_CONTINUE | SPVES_VOLUME), "got %#lx.\n", actions); + + hr = ISpTTSEngineSite_GetVolume(site, &engine->volume); + ok(hr == S_OK, "got %#lx.\n", hr); + actions = ISpTTSEngineSite_GetActions(site); + ok(actions == SPVES_CONTINUE, "got %#lx.\n", actions); + buf = calloc(1, 22050 * 2 / 5); for (i = 0; i < 5; i++) { + if (ISpTTSEngineSite_GetActions(site) & SPVES_ABORT) + break; hr = ISpTTSEngineSite_Write(site, buf, 22050 * 2 / 5, NULL); - ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK || hr == SP_AUDIO_STOPPED, "got %#lx.\n", hr); Sleep(100); } free(buf); @@ -507,7 +527,10 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(stream_num == 0xdeadbeef, "got %lu.\n", stream_num); - test_engine.speak_called = FALSE; + ISpVoice_SetRate(voice, 0); + ISpVoice_SetVolume(voice, 100); + + reset_engine_params(&test_engine); stream_num = 0xdeadbeef; start = GetTickCount(); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, &stream_num); @@ -520,6 +543,8 @@ static void test_spvoice(void) ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); + ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); ok(stream_num == 1, "got %lu.\n", stream_num); ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); @@ -540,6 +565,21 @@ static void test_spvoice(void) ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); + ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); + + Sleep(2000); + + reset_engine_params(&test_engine); + hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + Sleep(200); + start = GetTickCount(); + hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL); + duration = GetTickCount() - start; + ok(hr == S_OK, "got %#lx.\n", hr); + ok(duration < 300, "took %lu ms.\n", duration); done: reset_engine_params(&test_engine); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index acbef907646..432a021ea1c 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -45,6 +45,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; LONG cur_stream_num; + DWORD actions; USHORT volume; LONG rate; struct async_queue queue; @@ -777,6 +778,13 @@ static void speak_proc(struct async_task *task) EnterCriticalSection(&This->cs); + if (This->actions & SPVES_ABORT) + { + LeaveCriticalSection(&This->cs); + hr = S_OK; + goto done; + } + if (FAILED(hr = set_output_format(This->output, This->engine, &fmtid, &wfx))) { LeaveCriticalSection(&This->cs); @@ -789,6 +797,8 @@ static void speak_proc(struct async_task *task) if (SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio))) ISpAudio_SetState(audio, SPAS_RUN, 0); + This->actions = SPVES_RATE | SPVES_VOLUME; + LeaveCriticalSection(&This->cs); hr = ISpTTSEngine_Speak(engine, speak_task->flags, &fmtid, wfx, speak_task->frag_list, speak_task->site); @@ -847,6 +857,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR EnterCriticalSection(&This->cs); + This->actions = SPVES_ABORT; if (This->output && SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio))) { ISpAudio_SetState(audio, SPAS_CLOSED, 0); @@ -857,6 +868,10 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR async_empty_queue(&This->queue); + EnterCriticalSection(&This->cs); + This->actions = SPVES_CONTINUE; + LeaveCriticalSection(&This->cs); + if (!contents || !*contents) return S_OK; } @@ -1007,6 +1022,7 @@ static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG rate) EnterCriticalSection(&This->cs); This->rate = rate; + This->actions |= SPVES_RATE; LeaveCriticalSection(&This->cs); return S_OK; @@ -1036,6 +1052,7 @@ static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume) EnterCriticalSection(&This->cs); This->volume = volume; + This->actions |= SPVES_VOLUME; LeaveCriticalSection(&This->cs); return S_OK; @@ -1205,9 +1222,16 @@ static HRESULT WINAPI ttsenginesite_GetEventInterest(ISpTTSEngineSite *iface, UL static DWORD WINAPI ttsenginesite_GetActions(ISpTTSEngineSite *iface) { - FIXME("(%p): stub.\n", iface); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); + DWORD actions; + + TRACE("(%p).\n", iface); + + EnterCriticalSection(&This->voice->cs); + actions = This->voice->actions; + LeaveCriticalSection(&This->voice->cs); - return SPVES_CONTINUE; + return actions; } static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *buf, ULONG cb, ULONG *cb_written) @@ -1224,16 +1248,30 @@ static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *b static HRESULT WINAPI ttsenginesite_GetRate(ISpTTSEngineSite *iface, LONG *rate) { - FIXME("(%p, %p): stub.\n", iface, rate); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, rate); + + EnterCriticalSection(&This->voice->cs); + *rate = This->voice->rate; + This->voice->actions &= ~SPVES_RATE; + LeaveCriticalSection(&This->voice->cs); + + return S_OK; } static HRESULT WINAPI ttsenginesite_GetVolume(ISpTTSEngineSite *iface, USHORT *volume) { - FIXME("(%p, %p): stub.\n", iface, volume); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, volume); + + EnterCriticalSection(&This->voice->cs); + *volume = This->voice->volume; + This->voice->actions &= ~SPVES_VOLUME; + LeaveCriticalSection(&This->voice->cs); + + return S_OK; } static HRESULT WINAPI ttsenginesite_GetSkipInfo(ISpTTSEngineSite *iface, SPVSKIPTYPE *type, LONG *skip_count) @@ -1351,6 +1389,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; This->cur_stream_num = 0; + This->actions = SPVES_CONTINUE; This->volume = 100; This->rate = 0; memset(&This->queue, 0, sizeof(This->queue)); From dc8364437356c174d038564dbca361b61a5bf858 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 20 Jul 2023 17:24:56 -0400 Subject: [PATCH 656/758] sapi: Return wait status in async_wait_queue_empty. (cherry picked from commit b89c5361bbf7cd14b2e62194da4242069ee7a8c8) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/async.c | 6 +++--- dlls/sapi/sapi_private.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index 491ca657c1a..b02778ae7ba 100644 --- a/dlls/sapi/async.c +++ b/dlls/sapi/async.c @@ -171,8 +171,8 @@ HRESULT async_queue_task(struct async_queue *queue, struct async_task *task) return S_OK; } -void async_wait_queue_empty(struct async_queue *queue, DWORD timeout) +HRESULT async_wait_queue_empty(struct async_queue *queue, DWORD timeout) { - if (!queue->init) return; - WaitForSingleObject(queue->empty, timeout); + if (!queue->init) return WAIT_OBJECT_0; + return WaitForSingleObject(queue->empty, timeout); } diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index d0cd7669623..d38efb73b2e 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -42,7 +42,7 @@ HRESULT async_start_queue(struct async_queue *queue); void async_empty_queue(struct async_queue *queue); void async_cancel_queue(struct async_queue *queue); HRESULT async_queue_task(struct async_queue *queue, struct async_task *task); -void async_wait_queue_empty(struct async_queue *queue, DWORD timeout); +HRESULT async_wait_queue_empty(struct async_queue *queue, DWORD timeout); HRESULT data_key_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; HRESULT file_stream_create( IUnknown *outer, REFIID iid, void **obj ) DECLSPEC_HIDDEN; From c26e1cbec73325b476d1f0db1139a18572cfc6e9 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 20 Jul 2023 17:27:12 -0400 Subject: [PATCH 657/758] sapi: Implement ISpVoice::WaitUntilDone. (cherry picked from commit 61ad9174e28530b9e31daf7683ce17dfa68287e3) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 17 ++++++++++++++--- dlls/sapi/tts.c | 11 +++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 86a9312a37b..39f62ac551e 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -548,6 +548,12 @@ static void test_spvoice(void) ok(stream_num == 1, "got %lu.\n", stream_num); ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); + start = GetTickCount(); + hr = ISpVoice_WaitUntilDone(voice, INFINITE); + duration = GetTickCount() - start; + ok(hr == S_OK, "got %#lx.\n", hr); + ok(duration < 200, "took %lu ms.\n", duration); + reset_engine_params(&test_engine); stream_num = 0xdeadbeef; start = GetTickCount(); @@ -557,7 +563,14 @@ static void test_spvoice(void) todo_wine ok(stream_num == 1, "got %lu.\n", stream_num); ok(duration < 500, "took %lu ms.\n", duration); - Sleep(200); + hr = ISpVoice_WaitUntilDone(voice, 100); + ok(hr == S_FALSE, "got %#lx.\n", hr); + + hr = ISpVoice_WaitUntilDone(voice, INFINITE); + duration = GetTickCount() - start; + ok(hr == S_OK, "got %#lx.\n", hr); + ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); + ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags); ok(test_engine.frag_list != NULL, "frag_list is NULL.\n"); @@ -568,8 +581,6 @@ static void test_spvoice(void) ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); - Sleep(2000); - reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC, NULL); ok(hr == S_OK, "got %#lx.\n", hr); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 432a021ea1c..4763ef9f324 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -1073,9 +1073,16 @@ static HRESULT WINAPI spvoice_GetVolume(ISpVoice *iface, USHORT *volume) static HRESULT WINAPI spvoice_WaitUntilDone(ISpVoice *iface, ULONG timeout) { - FIXME("(%p, %ld): stub.\n", iface, timeout); + struct speech_voice *This = impl_from_ISpVoice(iface); + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %ld).\n", iface, timeout); + + hr = async_wait_queue_empty(&This->queue, timeout); + + if (hr == WAIT_OBJECT_0) return S_OK; + else if (hr == WAIT_TIMEOUT) return S_FALSE; + return hr; } static HRESULT WINAPI spvoice_SetSyncSpeakTimeout(ISpVoice *iface, ULONG timeout) From b6239e60ae824d4c1885fef7e3e5b562b03ffa6d Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 24 Jul 2023 13:19:04 +0300 Subject: [PATCH 658/758] wine.inf.in: Use native vulkan-1 for RDR2. So we don't depend only on the app id and play manifest to have the game playable. --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 1e8c569a950..d497ae555b2 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2881,3 +2881,4 @@ HKCU,Software\Wine\AppDefaults\Pentiment.exe\DllOverrides,"SpeechSynthesisWrappe HKCU,Software\Wine\AppDefaults\Maine-Win64-Shipping.exe\DllOverrides,"SpeechSynthWrapper",0x2,"disabled" HKCU,Software\Wine\AppDefaults\rayne1.exe\DllOverrides,"d3d8",,"native" HKCU,Software\Wine\AppDefaults\rayne2.exe\DllOverrides,"d3d8",,"native" +HKCU,Software\Wine\AppDefaults\RDR2.exe\DllOverrides,"vulkan-1",,"native" From e7ef13af744ba2eae0d748b0e1cc6b26a40419d2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 24 Jul 2023 19:38:56 -0600 Subject: [PATCH 659/758] nsiproxy.sys: Detect wireless interface type on Linux. CW-Bug-Id: #22496 --- configure.ac | 1 + dlls/nsiproxy.sys/ndis.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9e9c9ada9bb..c58fced1539 100644 --- a/configure.ac +++ b/configure.ac @@ -432,6 +432,7 @@ AC_CHECK_HEADERS(\ linux/serial.h \ linux/types.h \ linux/ucdrom.h \ + linux/wireless.h \ lwp.h \ mach-o/loader.h \ mach/mach.h \ diff --git a/dlls/nsiproxy.sys/ndis.c b/dlls/nsiproxy.sys/ndis.c index 1fc66c3611b..3d3791d3560 100644 --- a/dlls/nsiproxy.sys/ndis.c +++ b/dlls/nsiproxy.sys/ndis.c @@ -62,6 +62,10 @@ #include #endif +#ifdef HAVE_LINUX_WIRELESS_H +#include +#endif + #include #define NONAMELESSUNION @@ -173,13 +177,27 @@ static NTSTATUS if_get_physical( const char *name, UINT *type, IF_PHYSICAL_ADDRE if (*type == MIB_IF_TYPE_OTHER && !ioctl( fd, SIOCGIFFLAGS, &ifr ) && ifr.ifr_flags & IFF_POINTOPOINT) *type = MIB_IF_TYPE_PPP; +#ifdef HAVE_LINUX_WIRELESS_H + if (*type == MIB_IF_TYPE_ETHERNET) + { + struct iwreq pwrq; + + memset( &pwrq, 0, sizeof(pwrq) ); + memcpy( pwrq.ifr_name, name, size ); + if (ioctl( fd, SIOCGIWNAME, &pwrq ) != -1) + { + TRACE( "iface %s, wireless protocol %s.\n", debugstr_a(name), debugstr_a(pwrq.u.name) ); + *type = IF_TYPE_IEEE80211; + } + } +#endif + err: close( fd ); return ret; } #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H) - static NTSTATUS if_get_physical( const char *name, UINT *type, IF_PHYSICAL_ADDRESS *phys_addr ) { struct if_msghdr *ifm; From f8df24c74dd012234e38caa46b5f55299908c612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Jul 2023 10:38:35 +0200 Subject: [PATCH 660/758] winex11: Use XFixes to hide cursor before warping it. XWayland only allows warping the cursor if it is not visible. Fixes a regression from the periodic cursor sync removal. CW-Bug-Id: #21879 --- dlls/winex11.drv/mouse.c | 4 +++- dlls/winex11.drv/x11drv_main.c | 4 ++++ dlls/winex11.drv/xfixes.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index dd362e18e3d..72a5d0173e4 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -55,6 +55,7 @@ MAKE_FUNCPTR(XcursorLibraryLoadCursor); #define OEMRESOURCE #include "x11drv.h" +#include "xfixes.h" #include "winreg.h" #include "wine/server.h" #include "wine/debug.h" @@ -1470,9 +1471,10 @@ BOOL X11DRV_SetCursorPos( INT x, INT y ) TRACE( "real setting to %s\n", wine_dbgstr_point( &pos ) ); + pXFixesHideCursor( data->display, root_window ); XWarpPointer( data->display, root_window, root_window, 0, 0, 0, 0, pos.x, pos.y ); data->warp_serial = NextRequest( data->display ); - XNoOp( data->display ); + pXFixesShowCursor( data->display, root_window ); XFlush( data->display ); /* avoids bad mouse lag in games that do their own mouse warping */ TRACE( "warped to (fake) %d,%d serial %lu\n", x, y, data->warp_serial ); return TRUE; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 3a0f2a0e48e..d1257701b9d 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -629,11 +629,13 @@ static void X11DRV_XComposite_Init(void) #ifdef SONAME_LIBXFIXES #define MAKE_FUNCPTR(f) typeof(f) * p##f; +MAKE_FUNCPTR(XFixesHideCursor) MAKE_FUNCPTR(XFixesQueryExtension) MAKE_FUNCPTR(XFixesQueryVersion) MAKE_FUNCPTR(XFixesCreateRegion) MAKE_FUNCPTR(XFixesCreateRegionFromGC) MAKE_FUNCPTR(XFixesSelectSelectionInput) +MAKE_FUNCPTR(XFixesShowCursor) #undef MAKE_FUNCPTR static void x11drv_load_xfixes(void) @@ -654,11 +656,13 @@ static void x11drv_load_xfixes(void) dlclose(xfixes); \ return; \ } + LOAD_FUNCPTR(XFixesHideCursor) LOAD_FUNCPTR(XFixesQueryExtension) LOAD_FUNCPTR(XFixesQueryVersion) LOAD_FUNCPTR(XFixesCreateRegion) LOAD_FUNCPTR(XFixesCreateRegionFromGC) LOAD_FUNCPTR(XFixesSelectSelectionInput) + LOAD_FUNCPTR(XFixesShowCursor) #undef LOAD_FUNCPTR if (!pXFixesQueryExtension(gdi_display, &event, &error)) diff --git a/dlls/winex11.drv/xfixes.h b/dlls/winex11.drv/xfixes.h index 3ab31201d3d..10c9543ce3c 100644 --- a/dlls/winex11.drv/xfixes.h +++ b/dlls/winex11.drv/xfixes.h @@ -27,9 +27,11 @@ #ifdef SONAME_LIBXFIXES #include #define MAKE_FUNCPTR(f) extern typeof(f) * p##f DECLSPEC_HIDDEN; +MAKE_FUNCPTR(XFixesHideCursor) MAKE_FUNCPTR(XFixesQueryExtension) MAKE_FUNCPTR(XFixesQueryVersion) MAKE_FUNCPTR(XFixesSelectSelectionInput) +MAKE_FUNCPTR(XFixesShowCursor) #undef MAKE_FUNCPTR #endif /* defined(SONAME_LIBXFIXES) */ From 29b624a06a4296ec1c7e088801d912eaf32f5567 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 17:37:12 -0600 Subject: [PATCH 661/758] nsi: Add stub for NsiRequestChangeNotification. CW-Bug-Id: #22496 --- dlls/nsi/nsi.c | 8 ++++++++ dlls/nsi/nsi.spec | 2 +- include/wine/nsi.h | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index 3f324ef555b..d7dd9f09090 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -307,3 +307,11 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) CloseHandle( device ); return err; } + +DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, + HANDLE *handle ) +{ + FIXME( "%lu %p %lu %p %p stub.\n", unk, module, table, ovr, handle ); + + return ERROR_NOT_SUPPORTED; +} diff --git a/dlls/nsi/nsi.spec b/dlls/nsi/nsi.spec index ba326572fb8..ca2dc21168c 100644 --- a/dlls/nsi/nsi.spec +++ b/dlls/nsi/nsi.spec @@ -16,7 +16,7 @@ @ stdcall NsiGetParameterEx(ptr) @ stub NsiRegisterChangeNotification @ stub NsiRegisterChangeNotificationEx -@ stub NsiRequestChangeNotification +@ stdcall NsiRequestChangeNotification(long ptr long ptr ptr) @ stub NsiRequestChangeNotificationEx @ stub NsiSetAllParameters @ stub NsiSetAllParametersEx diff --git a/include/wine/nsi.h b/include/wine/nsi.h index af35593b29c..30713ae4a80 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -508,5 +508,7 @@ DWORD WINAPI NsiGetAllParametersEx( struct nsi_get_all_parameters_ex *params ); DWORD WINAPI NsiGetParameter( DWORD unk, const NPI_MODULEID *module, DWORD table, const void *key, DWORD key_size, DWORD param_type, void *data, DWORD data_size, DWORD data_offset ); DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ); +DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, + HANDLE *handle ); #endif /* __WINE_NSI_H */ From 18bed1e2c6366fd5f6a1a66906afacd3a44087a0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 17:39:41 -0600 Subject: [PATCH 662/758] nsi: Add stub for NsiCancelChangeNotification. CW-Bug-Id: #22496 --- dlls/nsi/nsi.c | 7 +++++++ dlls/nsi/nsi.spec | 2 +- include/wine/nsi.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index d7dd9f09090..76193c42982 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -315,3 +315,10 @@ DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module return ERROR_NOT_SUPPORTED; } + +DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ) +{ + FIXME( "%p stub.\n", ovr ); + + return ERROR_NOT_SUPPORTED; +} diff --git a/dlls/nsi/nsi.spec b/dlls/nsi/nsi.spec index ca2dc21168c..ed391e98138 100644 --- a/dlls/nsi/nsi.spec +++ b/dlls/nsi/nsi.spec @@ -1,6 +1,6 @@ @ stub NsiAllocateAndGetPersistentDataWithMaskTable @ stdcall NsiAllocateAndGetTable(long ptr long ptr long ptr long ptr long ptr long ptr long) -@ stub NsiCancelChangeNotification +@ stdcall NsiCancelChangeNotification(ptr) @ stub NsiDeregisterChangeNotification @ stub NsiDeregisterChangeNotificationEx @ stdcall NsiEnumerateObjectsAllParameters(long long ptr long ptr long ptr long ptr long ptr long ptr) diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 30713ae4a80..ee83dbb82e5 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -510,5 +510,6 @@ DWORD WINAPI NsiGetParameter( DWORD unk, const NPI_MODULEID *module, DWORD table DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ); DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, HANDLE *handle ); +DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ); #endif /* __WINE_NSI_H */ From 34c78e1a1cefac42fcdb9c741678871339153ad0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 17:35:08 -0600 Subject: [PATCH 663/758] nsi/tests: Add test for change notifications. CW-Bug-Id: #22496 --- dlls/nsi/tests/nsi.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 4cf49993ea1..d1aa7071d06 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "winsock2.h" #include "winternl.h" #include "ws2ipdef.h" @@ -1023,6 +1025,60 @@ static void test_udp_tables( int family ) winetest_pop_context(); } +void test_change_notifications(void) +{ + HANDLE handle, handle2; + OVERLAPPED ovr, ovr2; + DWORD bytes; + DWORD ret; + BOOL bret; + + memset( &ovr, 0, sizeof(ovr) ); + ovr.hEvent = CreateEventW( NULL, FALSE, FALSE, NULL ); + + handle = (HANDLE)0xdeadbeef; + ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr, &handle ); + todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + + handle2 = (HANDLE)0xdeadbeef; + memset( &ovr2, 0, sizeof(ovr2) ); + ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr2, &handle2 ); + todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + + ok( handle2 == handle, "got %p, %p.\n", handle, handle2 ); + bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); + + ret = NsiCancelChangeNotification( NULL ); + todo_wine ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret ); + + ret = NsiCancelChangeNotification( &ovr ); + todo_wine ok( !ret, "got %lu.\n", ret ); + + bytes = 0xdeadbeef; + bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + todo_wine ok( ovr.Internal == (ULONG)STATUS_CANCELLED, "got %Ix.\n", ovr.Internal ); + ok( !bytes, "got %lu.\n", bytes ); + + bret = GetOverlappedResult( handle2, &ovr2, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); + ret = NsiCancelChangeNotification( &ovr2 ); + todo_wine ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + + ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_INDEX_LUID_TABLE, &ovr, &handle ); + todo_wine ok( ret == ERROR_INVALID_PARAMETER, "got %lu.\n", ret ); + + ret = NsiRequestChangeNotification( 0, &NPI_MS_IPV4_MODULEID, NSI_IP_FORWARD_TABLE, &ovr, &handle ); + todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ret = NsiCancelChangeNotification( &ovr ); + todo_wine ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); +} + START_TEST( nsi ) { test_nsi_api(); @@ -1056,4 +1112,6 @@ START_TEST( nsi ) test_udp_stats( AF_INET6 ); test_udp_tables( AF_INET ); test_udp_tables( AF_INET6 ); + + test_change_notifications(); } From fce8aa4ead5552f196175a3e60ce190b74258c60 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 17:49:07 -0600 Subject: [PATCH 664/758] nsi: Cache nsi device handle. CW-Bug-Id: #22496 --- dlls/nsi/nsi.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index 76193c42982..9cb8e7d451b 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -30,9 +30,34 @@ WINE_DEFAULT_DEBUG_CHANNEL(nsi); +static HANDLE nsi_device = INVALID_HANDLE_VALUE; + +BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( hinst ); + break; + case DLL_PROCESS_DETACH: + if (nsi_device != INVALID_HANDLE_VALUE) CloseHandle( nsi_device ); + break; + } + return TRUE; +} + static inline HANDLE get_nsi_device( void ) { - return CreateFileW( L"\\\\.\\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + HANDLE device; + + if (nsi_device == INVALID_HANDLE_VALUE) + { + device = CreateFileW( L"\\\\.\\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + if (device != INVALID_HANDLE_VALUE + && InterlockedCompareExchangePointer( &nsi_device, device, INVALID_HANDLE_VALUE ) != INVALID_HANDLE_VALUE) + CloseHandle( device ); + } + return nsi_device; } DWORD WINAPI NsiAllocateAndGetTable( DWORD unk, const NPI_MODULEID *module, DWORD table, void **key_data, DWORD key_size, @@ -133,11 +158,7 @@ DWORD WINAPI NsiEnumerateObjectsAllParametersEx( struct nsi_enumerate_all_ex *pa (params->key_size + params->rw_size + params->dynamic_size + params->static_size) * params->count; out = heap_alloc( out_size ); - if (!out) - { - CloseHandle( device ); - return ERROR_OUTOFMEMORY; - } + if (!out) return ERROR_OUTOFMEMORY; in.module = *params->module; in.first_arg = params->first_arg; @@ -165,7 +186,6 @@ DWORD WINAPI NsiEnumerateObjectsAllParametersEx( struct nsi_enumerate_all_ex *pa } heap_free( out ); - CloseHandle( device ); return err; } @@ -249,7 +269,6 @@ DWORD WINAPI NsiGetAllParametersEx( struct nsi_get_all_parameters_ex *params ) err: heap_free( out ); heap_free( in ); - CloseHandle( device ); return err; } @@ -286,11 +305,7 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) if (device == INVALID_HANDLE_VALUE) return GetLastError(); in = heap_alloc( in_size ); - if (!in) - { - err = ERROR_OUTOFMEMORY; - goto err; - } + if (!in) return ERROR_OUTOFMEMORY; in->module = *params->module; in->first_arg = params->first_arg; in->table = params->table; @@ -302,9 +317,7 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) if (!DeviceIoControl( device, IOCTL_NSIPROXY_WINE_GET_PARAMETER, in, in_size, params->data, params->data_size, &received, NULL )) err = GetLastError(); -err: heap_free( in ); - CloseHandle( device ); return err; } From 5091f507f332a81918d9b98bc3aab7f8774f6d4e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 19:26:18 -0600 Subject: [PATCH 665/758] nsi: Forward request to nsiproxy from NsiRequestChangeNotification(). CW-Bug-Id: #22496 --- dlls/nsi/nsi.c | 54 +++++++++++++++++++++++++++++++------- dlls/nsi/tests/nsi.c | 13 ++++----- dlls/nsiproxy.sys/device.c | 46 ++++++++++++++++++++++++++++++++ include/wine/nsi.h | 8 ++++++ 4 files changed, 105 insertions(+), 16 deletions(-) diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index 9cb8e7d451b..fafb5ea5053 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -31,6 +31,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(nsi); static HANDLE nsi_device = INVALID_HANDLE_VALUE; +static HANDLE nsi_device_async = INVALID_HANDLE_VALUE; BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) { @@ -41,23 +42,25 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) break; case DLL_PROCESS_DETACH: if (nsi_device != INVALID_HANDLE_VALUE) CloseHandle( nsi_device ); + if (nsi_device_async != INVALID_HANDLE_VALUE) CloseHandle( nsi_device_async ); break; } return TRUE; } -static inline HANDLE get_nsi_device( void ) +static inline HANDLE get_nsi_device( BOOL async ) { + HANDLE *cached_device = async ? &nsi_device_async : &nsi_device; HANDLE device; - if (nsi_device == INVALID_HANDLE_VALUE) + if (*cached_device == INVALID_HANDLE_VALUE) { - device = CreateFileW( L"\\\\.\\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + device = CreateFileW( L"\\\\.\\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); if (device != INVALID_HANDLE_VALUE - && InterlockedCompareExchangePointer( &nsi_device, device, INVALID_HANDLE_VALUE ) != INVALID_HANDLE_VALUE) + && InterlockedCompareExchangePointer( cached_device, device, INVALID_HANDLE_VALUE ) != INVALID_HANDLE_VALUE) CloseHandle( device ); } - return nsi_device; + return *cached_device; } DWORD WINAPI NsiAllocateAndGetTable( DWORD unk, const NPI_MODULEID *module, DWORD table, void **key_data, DWORD key_size, @@ -148,7 +151,7 @@ DWORD WINAPI NsiEnumerateObjectsAllParameters( DWORD unk, DWORD unk2, const NPI_ DWORD WINAPI NsiEnumerateObjectsAllParametersEx( struct nsi_enumerate_all_ex *params ) { DWORD out_size, received, err = ERROR_SUCCESS; - HANDLE device = get_nsi_device(); + HANDLE device = get_nsi_device( FALSE ); struct nsiproxy_enumerate_all in; BYTE *out, *ptr; @@ -228,7 +231,7 @@ DWORD WINAPI NsiGetAllParameters( DWORD unk, const NPI_MODULEID *module, DWORD t DWORD WINAPI NsiGetAllParametersEx( struct nsi_get_all_parameters_ex *params ) { - HANDLE device = get_nsi_device(); + HANDLE device = get_nsi_device( FALSE ); struct nsiproxy_get_all_parameters *in; ULONG in_size = FIELD_OFFSET( struct nsiproxy_get_all_parameters, key[params->key_size] ), received; ULONG out_size = params->rw_size + params->dynamic_size + params->static_size; @@ -297,7 +300,7 @@ DWORD WINAPI NsiGetParameter( DWORD unk, const NPI_MODULEID *module, DWORD table DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) { - HANDLE device = get_nsi_device(); + HANDLE device = get_nsi_device( FALSE ); struct nsiproxy_get_parameter *in; ULONG in_size = FIELD_OFFSET( struct nsiproxy_get_parameter, key[params->key_size] ), received; DWORD err = ERROR_SUCCESS; @@ -324,9 +327,40 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, HANDLE *handle ) { - FIXME( "%lu %p %lu %p %p stub.\n", unk, module, table, ovr, handle ); + HANDLE device = get_nsi_device( TRUE ); + struct nsiproxy_request_notification *in; + ULONG in_size = sizeof(struct nsiproxy_get_parameter), received; + DWORD err = ERROR_SUCCESS; + OVERLAPPED overlapped; + DWORD len; - return ERROR_NOT_SUPPORTED; + TRACE( "%lu %p %lu %p %p.\n", unk, module, table, ovr, handle ); + + if (device == INVALID_HANDLE_VALUE) return GetLastError(); + + in = malloc( in_size ); + if (!in) return ERROR_OUTOFMEMORY; + in->module = *module; + in->table = table; + + if (!ovr) + { + overlapped.hEvent = CreateEventW( NULL, FALSE, FALSE, NULL ); + ovr = &overlapped; + } + if (!DeviceIoControl( device, IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION, in, in_size, NULL, 0, &received, ovr )) + err = GetLastError(); + if (ovr == &overlapped) + { + if (err == ERROR_IO_PENDING) + err = GetOverlappedResult( device, ovr, &len, TRUE ) ? 0 : GetLastError(); + CloseHandle( overlapped.hEvent ); + } + else if (handle && ovr && err == ERROR_IO_PENDING) + *handle = device; + + free( in ); + return err; } DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ) diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index d1aa7071d06..744b508d373 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -1038,16 +1038,16 @@ void test_change_notifications(void) handle = (HANDLE)0xdeadbeef; ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr, &handle ); - todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); handle2 = (HANDLE)0xdeadbeef; memset( &ovr2, 0, sizeof(ovr2) ); ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr2, &handle2 ); - todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); ok( handle2 == handle, "got %p, %p.\n", handle, handle2 ); bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); - todo_wine ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiCancelChangeNotification( NULL ); todo_wine ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret ); @@ -1057,12 +1057,13 @@ void test_change_notifications(void) bytes = 0xdeadbeef; bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); todo_wine ok( ovr.Internal == (ULONG)STATUS_CANCELLED, "got %Ix.\n", ovr.Internal ); - ok( !bytes, "got %lu.\n", bytes ); + todo_wine ok( !bytes, "got %lu.\n", bytes ); bret = GetOverlappedResult( handle2, &ovr2, &bytes, FALSE ); - todo_wine ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiCancelChangeNotification( &ovr2 ); todo_wine ok( !ret, "got %lu.\n", ret ); bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); @@ -1072,7 +1073,7 @@ void test_change_notifications(void) todo_wine ok( ret == ERROR_INVALID_PARAMETER, "got %lu.\n", ret ); ret = NsiRequestChangeNotification( 0, &NPI_MS_IPV4_MODULEID, NSI_IP_FORWARD_TABLE, &ovr, &handle ); - todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); ret = NsiCancelChangeNotification( &ovr ); todo_wine ok( !ret, "got %lu.\n", ret ); bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 4528e934991..f0472b3fe7f 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -49,6 +49,7 @@ DECLARE_CRITICAL_SECTION( nsiproxy_cs ); #define LIST_ENTRY_INIT( list ) { .Flink = &(list), .Blink = &(list) } static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue ); +static LIST_ENTRY notification_queue = LIST_ENTRY_INIT( notification_queue ); static NTSTATUS nsiproxy_call( unsigned int code, void *args ) { @@ -261,6 +262,47 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) return STATUS_PENDING; } +static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp ) +{ + TRACE( "device %p, irp %p.\n", device, irp ); + + IoReleaseCancelSpinLock( irp->CancelIrql ); + + EnterCriticalSection( &nsiproxy_cs ); + RemoveEntryList( &irp->Tail.Overlay.ListEntry ); + LeaveCriticalSection( &nsiproxy_cs ); + + irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest( irp, IO_NO_INCREMENT ); +} + +static NTSTATUS nsiproxy_change_notification( IRP *irp ) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + struct nsiproxy_request_notification *in = (struct nsiproxy_request_notification *)irp->AssociatedIrp.SystemBuffer; + DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength; + + FIXME( "\n" ); + + if (in_len < sizeof(*in)) return STATUS_INVALID_PARAMETER; + /* FIXME: validate module and table. */ + + EnterCriticalSection( &nsiproxy_cs ); + IoSetCancelRoutine( irp, change_notification_cancel ); + if (irp->Cancel && !IoSetCancelRoutine( irp, NULL )) + { + /* IRP was canceled before we set cancel routine */ + InitializeListHead( &irp->Tail.Overlay.ListEntry ); + LeaveCriticalSection( &nsiproxy_cs ); + return STATUS_CANCELLED; + } + InsertTailList( ¬ification_queue, &irp->Tail.Overlay.ListEntry ); + IoMarkIrpPending( irp ); + LeaveCriticalSection( &nsiproxy_cs ); + + return STATUS_PENDING; +} + static NTSTATUS WINAPI nsi_ioctl( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); @@ -289,6 +331,10 @@ static NTSTATUS WINAPI nsi_ioctl( DEVICE_OBJECT *device, IRP *irp ) status = nsiproxy_icmp_echo( irp ); break; + case IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION: + status = nsiproxy_change_notification( irp ); + break; + default: FIXME( "ioctl %lx not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); status = STATUS_NOT_SUPPORTED; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index ee83dbb82e5..1a999fdcc18 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -380,6 +380,7 @@ struct nsi_udp_endpoint_static #define IOCTL_NSIPROXY_WINE_GET_ALL_PARAMETERS CTL_CODE(FILE_DEVICE_NETWORK, 0x401, METHOD_BUFFERED, 0) #define IOCTL_NSIPROXY_WINE_GET_PARAMETER CTL_CODE(FILE_DEVICE_NETWORK, 0x402, METHOD_BUFFERED, 0) #define IOCTL_NSIPROXY_WINE_ICMP_ECHO CTL_CODE(FILE_DEVICE_NETWORK, 0x403, METHOD_BUFFERED, 0) +#define IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION CTL_CODE(FILE_DEVICE_NETWORK, 0x404, METHOD_BUFFERED, 0) /* input for IOCTL_NSIPROXY_WINE_ENUMERATE_ALL */ struct nsiproxy_enumerate_all @@ -436,6 +437,13 @@ struct nsiproxy_icmp_echo BYTE data[1]; /* ((opt_size + 3) & ~3) + req_size */ }; +/* input for IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION */ +struct nsiproxy_request_notification +{ + NPI_MODULEID module; + UINT table; +}; + /* Undocumented Nsi api */ #define NSI_PARAM_TYPE_RW 0 From f27995a6f304d9060672578527abc6c233e51ff7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 19:28:25 -0600 Subject: [PATCH 666/758] nsi: Implement NsiCancelChangeNotification(). CW-Bug-Id: #22496 --- dlls/nsi/nsi.c | 10 ++++++++-- dlls/nsi/tests/nsi.c | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index fafb5ea5053..e2e0d2d0809 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -365,7 +365,13 @@ DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ) { - FIXME( "%p stub.\n", ovr ); + DWORD err = ERROR_SUCCESS; + + TRACE( "%p.\n", ovr ); - return ERROR_NOT_SUPPORTED; + if (!ovr) return ERROR_NOT_FOUND; + if (!CancelIoEx( get_nsi_device( TRUE ), ovr )) + err = GetLastError(); + + return err; } diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 744b508d373..0b8804acaad 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -1050,24 +1050,24 @@ void test_change_notifications(void) ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiCancelChangeNotification( NULL ); - todo_wine ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret ); + ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret ); ret = NsiCancelChangeNotification( &ovr ); - todo_wine ok( !ret, "got %lu.\n", ret ); + ok( !ret, "got %lu.\n", ret ); bytes = 0xdeadbeef; - bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + bret = GetOverlappedResult( handle, &ovr, &bytes, TRUE ); - todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); - todo_wine ok( ovr.Internal == (ULONG)STATUS_CANCELLED, "got %Ix.\n", ovr.Internal ); - todo_wine ok( !bytes, "got %lu.\n", bytes ); + ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( ovr.Internal == (ULONG)STATUS_CANCELLED, "got %Ix.\n", ovr.Internal ); + ok( !bytes, "got %lu.\n", bytes ); bret = GetOverlappedResult( handle2, &ovr2, &bytes, FALSE ); ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiCancelChangeNotification( &ovr2 ); - todo_wine ok( !ret, "got %lu.\n", ret ); - bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); - todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, TRUE ); + ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_INDEX_LUID_TABLE, &ovr, &handle ); todo_wine ok( ret == ERROR_INVALID_PARAMETER, "got %lu.\n", ret ); @@ -1075,9 +1075,9 @@ void test_change_notifications(void) ret = NsiRequestChangeNotification( 0, &NPI_MS_IPV4_MODULEID, NSI_IP_FORWARD_TABLE, &ovr, &handle ); ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); ret = NsiCancelChangeNotification( &ovr ); - todo_wine ok( !ret, "got %lu.\n", ret ); - bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); - todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, TRUE ); + ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); } START_TEST( nsi ) From da5e38905e9a833225123b0e7792062c15c351ee Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 26 Jul 2023 09:06:29 -0600 Subject: [PATCH 667/758] nsiproxy.sys: Implement change notifications for NSI_IP_UNICAST_TABLE. CW-Bug-Id: #22496 --- dlls/nsiproxy.sys/device.c | 56 +++++++++++- dlls/nsiproxy.sys/nsi.c | 122 ++++++++++++++++++++++++++- dlls/nsiproxy.sys/nsiproxy_private.h | 7 ++ 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index f0472b3fe7f..8acdecdb735 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -51,6 +51,12 @@ DECLARE_CRITICAL_SECTION( nsiproxy_cs ); static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue ); static LIST_ENTRY notification_queue = LIST_ENTRY_INIT( notification_queue ); +struct notification_data +{ + NPI_MODULEID module; + UINT table; +}; + static NTSTATUS nsiproxy_call( unsigned int code, void *args ) { return WINE_UNIX_CALL( code, args ); @@ -65,6 +71,7 @@ enum unix_calls nsi_enumerate_all_ex, nsi_get_all_parameters_ex, nsi_get_parameter_ex, + nsi_get_notification, }; static NTSTATUS nsiproxy_enumerate_all( IRP *irp ) @@ -270,6 +277,7 @@ static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp ) EnterCriticalSection( &nsiproxy_cs ); RemoveEntryList( &irp->Tail.Overlay.ListEntry ); + free( irp->Tail.Overlay.DriverContext[0] ); LeaveCriticalSection( &nsiproxy_cs ); irp->IoStatus.Status = STATUS_CANCELLED; @@ -281,10 +289,12 @@ static NTSTATUS nsiproxy_change_notification( IRP *irp ) IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); struct nsiproxy_request_notification *in = (struct nsiproxy_request_notification *)irp->AssociatedIrp.SystemBuffer; DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength; + struct notification_data *data; - FIXME( "\n" ); + TRACE( "irp %p.\n", irp ); if (in_len < sizeof(*in)) return STATUS_INVALID_PARAMETER; + if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY; /* FIXME: validate module and table. */ EnterCriticalSection( &nsiproxy_cs ); @@ -294,10 +304,14 @@ static NTSTATUS nsiproxy_change_notification( IRP *irp ) /* IRP was canceled before we set cancel routine */ InitializeListHead( &irp->Tail.Overlay.ListEntry ); LeaveCriticalSection( &nsiproxy_cs ); + free( data ); return STATUS_CANCELLED; } InsertTailList( ¬ification_queue, &irp->Tail.Overlay.ListEntry ); IoMarkIrpPending( irp ); + data->module = in->module; + data->table = in->table; + irp->Tail.Overlay.DriverContext[0] = data; LeaveCriticalSection( &nsiproxy_cs ); return STATUS_PENDING; @@ -468,6 +482,44 @@ static DWORD WINAPI request_thread_proc( void *arg ) return 0; } +static DWORD WINAPI notification_thread_proc( void *arg ) +{ + struct nsi_get_notification_params params; + LIST_ENTRY *entry, *next; + NTSTATUS status; + + while (!(status = nsiproxy_call( nsi_get_notification, ¶ms ))) + { + EnterCriticalSection( &nsiproxy_cs ); + for (entry = notification_queue.Flink; entry != ¬ification_queue; entry = next) + { + IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry ); + struct notification_data *data = irp->Tail.Overlay.DriverContext[0]; + + next = entry->Flink; + if(irp->Cancel) + { + /* Cancel routine should care of freeing data and completing IRP. */ + TRACE( "irp %p canceled.\n", irp ); + continue; + } + if (!NmrIsEqualNpiModuleId( &data->module, ¶ms.module ) || data->table != params.table) + continue; + + irp->IoStatus.Status = 0; + RemoveEntryList( entry ); + irp->Tail.Overlay.DriverContext[0] = NULL; + free( data ); + TRACE("completing irp %p.\n", irp); + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } + LeaveCriticalSection( &nsiproxy_cs ); + } + + WARN( "nsi_get_notification failed, status %#lx.\n", status ); + return 0; +} + NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) { NTSTATUS status; @@ -485,6 +537,8 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) request_event = CreateEventW( NULL, FALSE, FALSE, NULL ); thread = CreateThread( NULL, 0, request_thread_proc, NULL, 0, NULL ); CloseHandle( thread ); + thread = CreateThread( NULL, 0, notification_thread_proc, NULL, 0, NULL ); + CloseHandle( thread ); return STATUS_SUCCESS; } diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c index b9b04e63545..2f6d2d59573 100644 --- a/dlls/nsiproxy.sys/nsi.c +++ b/dlls/nsiproxy.sys/nsi.c @@ -21,7 +21,16 @@ #pragma makedep unix #endif +#include "config.h" #include +#include +#include +#include +#include +#include +#ifdef HAVE_LINUX_RTNETLINK_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -32,11 +41,13 @@ #include "ddk/wdm.h" #include "ifdef.h" #define __WINE_INIT_NPI_MODULEID +#define USE_WS_PREFIX #include "netiodef.h" #include "wine/nsi.h" #include "wine/debug.h" #include "wine/unixlib.h" #include "unix_private.h" +#include "nsiproxy_private.h" WINE_DEFAULT_DEBUG_CHANNEL(nsi); @@ -145,6 +156,114 @@ static NTSTATUS unix_nsi_get_parameter_ex( void *args ) return nsi_get_parameter_ex( params ); } +#ifdef HAVE_LINUX_RTNETLINK_H +static struct +{ + const NPI_MODULEID *module; + UINT32 table; +} +queued_notifications[256]; +static unsigned int queued_notification_count; + +static NTSTATUS add_notification( const NPI_MODULEID *module, UINT32 table ) +{ + unsigned int i; + + for (i = 0; i < queued_notification_count; ++i) + if (queued_notifications[i].module == module && queued_notifications[i].table == table) return STATUS_SUCCESS; + if (queued_notification_count == ARRAY_SIZE(queued_notifications)) + { + ERR( "Notification queue full.\n" ); + return STATUS_NO_MEMORY; + } + queued_notifications[i].module = module; + queued_notifications[i].table = table; + ++queued_notification_count; + return STATUS_SUCCESS; +} + +static NTSTATUS poll_netlink(void) +{ + static int netlink_fd = -1; + char buffer[PIPE_BUF]; + struct nlmsghdr *nlh; + NTSTATUS status; + int len; + + if (netlink_fd == -1) + { + struct sockaddr_nl addr; + + if ((netlink_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE )) == -1) + { + ERR( "netlink socket creation failed, errno %d.\n", errno ); + return STATUS_NOT_IMPLEMENTED; + } + + memset( &addr, 0, sizeof(addr) ); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + if (bind( netlink_fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1) + { + close( netlink_fd ); + netlink_fd = -1; + ERR( "bind failed, errno %d.\n", errno ); + return STATUS_NOT_IMPLEMENTED; + } + } + + while (1) + { + len = recv( netlink_fd, buffer, sizeof(buffer), 0 ); + if (len <= 0) + { + if (errno == EINTR) continue; + ERR( "error receivng, len %d, errno %d.\n", len, errno ); + return STATUS_UNSUCCESSFUL; + } + for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) + { + if (nlh->nlmsg_type == NLMSG_DONE) break; + if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR) + { + struct ifaddrmsg *addrmsg = (struct ifaddrmsg *)(nlh + 1); + const NPI_MODULEID *module; + + if (addrmsg->ifa_family == AF_INET) module = &NPI_MS_IPV4_MODULEID; + else if (addrmsg->ifa_family == AF_INET6) module = &NPI_MS_IPV6_MODULEID; + else + { + WARN( "Unknown addrmsg->ifa_family %d.\n", addrmsg->ifa_family ); + continue; + } + if ((status = add_notification( module, NSI_IP_UNICAST_TABLE))) return status; + } + } + if (queued_notification_count) break; + } + return STATUS_SUCCESS; +} + +static NTSTATUS unix_nsi_get_notification( void *args ) +{ + struct nsi_get_notification_params *params = (struct nsi_get_notification_params *)args; + NTSTATUS status; + + if (!queued_notification_count && (status = poll_netlink())) return status; + assert( queued_notification_count ); + params->module = *queued_notifications[0].module; + params->table = queued_notifications[0].table; + --queued_notification_count; + memmove( queued_notifications, queued_notifications + 1, sizeof(*queued_notifications) * queued_notification_count ); + return STATUS_SUCCESS; +} +#else +static NTSTATUS unix_nsi_get_notification( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} +#endif + const unixlib_entry_t __wine_unix_call_funcs[] = { icmp_cancel_listen, @@ -153,5 +272,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = icmp_send_echo, unix_nsi_enumerate_all_ex, unix_nsi_get_all_parameters_ex, - unix_nsi_get_parameter_ex + unix_nsi_get_parameter_ex, + unix_nsi_get_notification, }; diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 241106fe228..1b6eacf7d08 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -84,3 +84,10 @@ struct icmp_echo_reply_64 ULONGLONG options_ptr; } opts; }; + +struct nsi_get_notification_params +{ + /* output parameters */ + NPI_MODULEID module; + UINT32 table; +}; From bd5a965b0e8ebb0c9b01cd6d5a99792b2753a7f5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 26 Jul 2023 19:19:30 -0600 Subject: [PATCH 668/758] iphlpapi: Link NotifyAddrChange and CancelIPChangeNotify to nsi implementation. CW-Bug-Id: #22496 --- dlls/iphlpapi/iphlpapi_main.c | 21 +++++++++------------ dlls/iphlpapi/tests/iphlpapi.c | 14 +++++++++----- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 6286c168277..9b0c125b425 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -128,14 +128,15 @@ DWORD WINAPI AddIPAddress(IPAddr Address, IPMask IpMask, DWORD IfIndex, PULONG N * RETURNS * Success: TRUE * Failure: FALSE - * - * FIXME - * Stub, returns FALSE. */ BOOL WINAPI CancelIPChangeNotify(LPOVERLAPPED overlapped) { - FIXME("(overlapped %p): stub\n", overlapped); - return FALSE; + DWORD err; + + TRACE("overlapped %p.\n", overlapped); + + if ((err = NsiCancelChangeNotification( overlapped ))) SetLastError( err ); + return !err; } @@ -3787,16 +3788,12 @@ DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo) * RETURNS * Success: NO_ERROR * Failure: error code from winerror.h - * - * FIXME - * Stub, returns ERROR_NOT_SUPPORTED. */ DWORD WINAPI NotifyAddrChange(PHANDLE Handle, LPOVERLAPPED overlapped) { - FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped); - if (Handle) *Handle = INVALID_HANDLE_VALUE; - if (overlapped) ((IO_STATUS_BLOCK *) overlapped)->Status = STATUS_PENDING; - return ERROR_IO_PENDING; + TRACE("Handle %p, overlapped %p.\n", Handle, overlapped); + + return NsiRequestChangeNotification(0, &NPI_MS_IPV4_MODULEID, NSI_IP_UNICAST_TABLE, overlapped, Handle); } diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index b8fa82d2a56..4dd2db9e1ee 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1670,9 +1670,11 @@ static void testNotifyAddrChange(void) ret = NotifyAddrChange(&handle, &overlapped); ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %ld, expected ERROR_IO_PENDING\n", ret); ret = GetLastError(); - todo_wine ok(ret == ERROR_IO_PENDING, "GetLastError returned %ld, expected ERROR_IO_PENDING\n", ret); + ok(ret == ERROR_IO_PENDING, "GetLastError returned %ld, expected ERROR_IO_PENDING\n", ret); success = CancelIPChangeNotify(&overlapped); - todo_wine ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + success = GetOverlappedResult( handle, &overlapped, &bytes, TRUE ); + ok( !success && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", success, GetLastError() ); ZeroMemory(&overlapped, sizeof(overlapped)); success = CancelIPChangeNotify(&overlapped); @@ -1683,13 +1685,15 @@ static void testNotifyAddrChange(void) overlapped.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); ret = NotifyAddrChange(&handle, &overlapped); ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %ld, expected ERROR_IO_PENDING\n", ret); - todo_wine ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n"); + ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n"); success = GetOverlappedResult(handle, &overlapped, &bytes, FALSE); ok(success == FALSE, "GetOverlappedResult returned TRUE, expected FALSE\n"); ret = GetLastError(); ok(ret == ERROR_IO_INCOMPLETE, "GetLastError returned %ld, expected ERROR_IO_INCOMPLETE\n", ret); success = CancelIPChangeNotify(&overlapped); - todo_wine ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + success = GetOverlappedResult( handle, &overlapped, &bytes, TRUE ); + ok( !success && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", success, GetLastError() ); if (winetest_interactive) { @@ -1710,7 +1714,7 @@ static void testNotifyAddrChange(void) trace("Testing synchronous ipv4 address change notification. Please " "change the ipv4 address of one of your network interfaces\n"); ret = NotifyAddrChange(NULL, NULL); - todo_wine ok(ret == NO_ERROR, "NotifyAddrChange returned %ld, expected NO_ERROR\n", ret); + ok(ret == NO_ERROR, "NotifyAddrChange returned %ld, expected NO_ERROR\n", ret); } } From 97bd8cc7c98eaa80413e064346e171c247d6dd61 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Mon, 5 Jun 2023 19:59:14 -0300 Subject: [PATCH 669/758] msvcr110/tests: Add tests for new setlocale behaviors. A bunch of locales had changes >= msvcr 110. (cherry picked from commit fbbe8e26be898770e98307ed3a9789ba06b5cff9) CW-Bug-Id: #19646 --- dlls/msvcr110/tests/msvcr110.c | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dlls/msvcr110/tests/msvcr110.c b/dlls/msvcr110/tests/msvcr110.c index 255c68b0796..cf76720d806 100644 --- a/dlls/msvcr110/tests/msvcr110.c +++ b/dlls/msvcr110/tests/msvcr110.c @@ -152,6 +152,84 @@ static void test_setlocale(void) ret = p_setlocale(LC_ALL, "en-us.1250"); ok(!ret, "setlocale(en-us.1250) succeeded (%s)\n", ret); + ret = p_setlocale(LC_ALL, "zh-Hans"); + todo_wine ok((ret != NULL + || broken(ret == NULL)), /* Vista */ + "expected success, but got NULL\n"); + if (ret) + ok(!strcmp(ret, "zh-Hans"), "setlocale zh-Hans failed\n"); + + ret = p_setlocale(LC_ALL, "zh-Hant"); + todo_wine ok((ret != NULL + || broken(ret == NULL)), /* Vista */ + "expected success, but got NULL\n"); + if (ret) + ok(!strcmp(ret, "zh-Hant"), "setlocale zh-Hant failed\n"); + + /* used to return Chinese (Simplified)_China.936 */ + ret = p_setlocale(LC_ALL, "chinese"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Chinese_China.936") + || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936")) /* Vista */ + || broken(!strcmp(ret, "Chinese_People's Republic of China.936"))), /* 7 */ + "setlocale chinese failed, got %s\n", ret); + + /* used to return Chinese (Simplified)_China.936 */ + ret = p_setlocale(LC_ALL, "Chinese_China.936"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Chinese_China.936") + || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936")) /* Vista */ + || broken(!strcmp(ret, "Chinese_People's Republic of China.936"))), /* 7 */ + "setlocale Chinese_China.936 failed, got %s\n", ret); + + /* used to return Chinese (Simplified)_China.936 */ + ret = p_setlocale(LC_ALL, "chinese-simplified"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Chinese_China.936") + || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936"))), /* Vista */ + "setlocale chinese-simplified failed, got %s\n", ret); + + /* used to return Chinese (Simplified)_China.936 */ + ret = p_setlocale(LC_ALL, "chs"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Chinese_China.936") + || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936"))), /* Vista */ + "setlocale chs failed, got %s\n", ret); + + /* used to return Chinese (Traditional)_Taiwan.950 */ + ret = p_setlocale(LC_ALL, "cht"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Chinese (Traditional)_Hong Kong SAR.950") + || broken(!strcmp(ret, "Chinese (Traditional)_Taiwan.950"))), /* Vista - 7 */ + "setlocale cht failed, got %s\n", ret); + + /* used to return Chinese (Traditional)_Taiwan.950 */ + ret = p_setlocale(LC_ALL, "chinese-traditional"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Chinese (Traditional)_Hong Kong SAR.950") + || broken(!strcmp(ret, "Chinese (Traditional)_Taiwan.950"))), /* Vista - 7 */ + "setlocale chinese-traditional failed, got %s\n", ret); + + ret = p_setlocale(LC_ALL, "norwegian-nynorsk"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") + || broken(!strcmp(ret, "Norwegian (Nynorsk)_Norway.1252"))), /* Vista - 7 */ + "setlocale norwegian-nynorsk failed, got %s\n", ret); + + ret = p_setlocale(LC_ALL, "non"); + ok(ret != NULL, "expected success, but got NULL\n"); + if (ret) + todo_wine ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") + || broken(!strcmp(ret, "Norwegian (Nynorsk)_Norway.1252"))), /* Vista - 7 */ + "setlocale norwegian-nynorsk failed, got %s\n", ret); + p_setlocale(LC_ALL, "C"); } From 9c5fbb61c9339a2b9166fdfd2c0414ecdde64ea4 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Wed, 7 Jun 2023 17:46:59 -0300 Subject: [PATCH 670/758] msvcr120/tests: Check ___lc_locale_name_func with neutral Chinese locales. (cherry picked from commit 969e3626bbcc7bd9ad6bca2d368f0bb67e306786) CW-Bug-Id: #19646 --- dlls/msvcr120/tests/msvcr120.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index 0a4fb383e0e..b8950288afe 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -596,6 +596,14 @@ static void test____lc_locale_name_func(void) } } + p_setlocale(LC_ALL, "zh-Hans"); + lc_names = p____lc_locale_name_func(); + todo_wine ok(!lstrcmpW(lc_names[1], L"zh-Hans"), "lc_names[1] expected zh-Hans got %s\n", wine_dbgstr_w(lc_names[1])); + + p_setlocale(LC_ALL, "zh-Hant"); + lc_names = p____lc_locale_name_func(); + todo_wine ok(!lstrcmpW(lc_names[1], L"zh-Hant"), "lc_names[1] expected zh-Hant got %s\n", wine_dbgstr_w(lc_names[1])); + p_setlocale(LC_ALL, "C"); lc_names = p____lc_locale_name_func(); ok(!lc_names[1], "___lc_locale_name_func()[1] = %s\n", wine_dbgstr_w(lc_names[1])); From bbfa2445a5b1f01443ce744acaa564c9b55fab5c Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Thu, 8 Jun 2023 15:14:57 -0300 Subject: [PATCH 671/758] msvcrt: Use snames instead of LCIDs in create_locinfo. (cherry picked from commit 24a2b625545f1875b5c3177f2b9da1b7299b864f) CW-Bug-Id: #19646 --- dlls/msvcr110/tests/msvcr110.c | 12 +- dlls/msvcrt/locale.c | 459 ++++++++++++++++++--------------- 2 files changed, 257 insertions(+), 214 deletions(-) diff --git a/dlls/msvcr110/tests/msvcr110.c b/dlls/msvcr110/tests/msvcr110.c index cf76720d806..60d876e3bfc 100644 --- a/dlls/msvcr110/tests/msvcr110.c +++ b/dlls/msvcr110/tests/msvcr110.c @@ -170,7 +170,7 @@ static void test_setlocale(void) ret = p_setlocale(LC_ALL, "chinese"); ok(ret != NULL, "expected success, but got NULL\n"); if (ret) - todo_wine ok((!strcmp(ret, "Chinese_China.936") + ok((!strcmp(ret, "Chinese_China.936") || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936")) /* Vista */ || broken(!strcmp(ret, "Chinese_People's Republic of China.936"))), /* 7 */ "setlocale chinese failed, got %s\n", ret); @@ -179,7 +179,7 @@ static void test_setlocale(void) ret = p_setlocale(LC_ALL, "Chinese_China.936"); ok(ret != NULL, "expected success, but got NULL\n"); if (ret) - todo_wine ok((!strcmp(ret, "Chinese_China.936") + ok((!strcmp(ret, "Chinese_China.936") || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936")) /* Vista */ || broken(!strcmp(ret, "Chinese_People's Republic of China.936"))), /* 7 */ "setlocale Chinese_China.936 failed, got %s\n", ret); @@ -188,7 +188,7 @@ static void test_setlocale(void) ret = p_setlocale(LC_ALL, "chinese-simplified"); ok(ret != NULL, "expected success, but got NULL\n"); if (ret) - todo_wine ok((!strcmp(ret, "Chinese_China.936") + ok((!strcmp(ret, "Chinese_China.936") || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936"))), /* Vista */ "setlocale chinese-simplified failed, got %s\n", ret); @@ -196,7 +196,7 @@ static void test_setlocale(void) ret = p_setlocale(LC_ALL, "chs"); ok(ret != NULL, "expected success, but got NULL\n"); if (ret) - todo_wine ok((!strcmp(ret, "Chinese_China.936") + ok((!strcmp(ret, "Chinese_China.936") || broken(!strcmp(ret, "Chinese (Simplified)_People's Republic of China.936"))), /* Vista */ "setlocale chs failed, got %s\n", ret); @@ -219,14 +219,14 @@ static void test_setlocale(void) ret = p_setlocale(LC_ALL, "norwegian-nynorsk"); ok(ret != NULL, "expected success, but got NULL\n"); if (ret) - todo_wine ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") + ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") || broken(!strcmp(ret, "Norwegian (Nynorsk)_Norway.1252"))), /* Vista - 7 */ "setlocale norwegian-nynorsk failed, got %s\n", ret); ret = p_setlocale(LC_ALL, "non"); ok(ret != NULL, "expected success, but got NULL\n"); if (ret) - todo_wine ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") + ok((!strcmp(ret, "Norwegian Nynorsk_Norway.1252") || broken(!strcmp(ret, "Norwegian (Nynorsk)_Norway.1252"))), /* Vista - 7 */ "setlocale norwegian-nynorsk failed, got %s\n", ret); diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 16ed68cdebd..845de26155c 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -51,6 +51,12 @@ BOOL initial_locale = TRUE; #define MSVCRT_LEADBYTE 0x8000 #define MSVCRT_C1_DEFINED 0x200 +#if _MSVCR_VER >= 110 +#define LCID_CONVERSION_FLAGS LOCALE_ALLOW_NEUTRAL_NAMES +#else +#define LCID_CONVERSION_FLAGS 0 +#endif + __lc_time_data cloc_time_data = { {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", @@ -258,7 +264,7 @@ static BOOL CALLBACK find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) { locale_search_t *res = (locale_search_t *)lParam; - const LCID lcid = LocaleNameToLCID( name, 0 ); + const LCID lcid = LocaleNameToLCID( name, LCID_CONVERSION_FLAGS ); char buff[MAX_ELEM_LEN]; unsigned int flags = 0; @@ -469,8 +475,8 @@ static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat) |LOCALE_NOUSEROVERRIDE, buf, 100); if(!len) return FALSE; - if(LocaleNameToLCID(buf, 0) != lcid) - len = LCIDToLocaleName(lcid, buf, 100, 0); + if(LocaleNameToLCID(buf, LOCALE_ALLOW_NEUTRAL_NAMES) != lcid) + len = LCIDToLocaleName(lcid, buf, 100, LOCALE_ALLOW_NEUTRAL_NAMES); if(!len || !(locinfo->lc_name[cat] = malloc(len*sizeof(wchar_t)))) return FALSE; @@ -486,13 +492,13 @@ static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat) #endif /* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */ -static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp, +static BOOL update_threadlocinfo_category(WCHAR *sname, unsigned short cp, pthreadlocinfo locinfo, int category) { - char buf[256], *p; + WCHAR wbuf[256], *p; - if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) { - p = buf; + if(GetLocaleInfoEx(sname, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf))) { + p = wbuf; locinfo->lc_id[category].wLanguage = 0; while(*p) { @@ -512,26 +518,32 @@ static BOOL update_threadlocinfo_category(LCID lcid, unsigned short cp, locinfo->lc_id[category].wCodePage = cp; - locinfo->lc_handle[category] = lcid; + locinfo->lc_handle[category] = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS); set_lc_locale_name(locinfo, category); if(!locinfo->lc_category[category].locale) { + char buf[256]; int len = 0; - if (lcid == MAKELANGID( LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK )) +#if _MSVCR_VER < 110 + if (LANGIDFROMLCID(locinfo->lc_handle[category]) == MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK)) { /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */ - strcpy( buf, "Norwegian-Nynorsk" ); - len = strlen( buf ) + 1; + wcscpy( wbuf, L"Norwegian-Nynorsk" ); + len = wcslen( wbuf ) + 1; } - else len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256); - buf[len-1] = '_'; - len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY - |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len); - buf[len-1] = '.'; - sprintf(buf+len, "%d", cp); - len += strlen(buf+len); + else +#endif + len += GetLocaleInfoEx(sname, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf)); + wbuf[len-1] = '_'; + len += GetLocaleInfoEx(sname, LOCALE_SENGCOUNTRY + |LOCALE_NOUSEROVERRIDE, &wbuf[len], ARRAY_SIZE(wbuf) - len); + wbuf[len-1] = '.'; + swprintf(wbuf+len, ARRAY_SIZE(wbuf) - len,L"%d", cp); + len += wcslen(wbuf+len); + + WideCharToMultiByte(cp, 0, wbuf, -1, buf, ARRAY_SIZE(buf), NULL, NULL); return init_category_name(buf, len, locinfo, category); } @@ -1161,13 +1173,22 @@ void CDECL _free_locale(_locale_t locale) } static inline BOOL category_needs_update(int cat, - const threadlocinfo *locinfo, LCID lcid, unsigned short cp) + const threadlocinfo *locinfo, WCHAR *sname, unsigned short cp) { +#if _MSVCR_VER < 110 + LCID lcid; +#endif if(!locinfo) return TRUE; +#if _MSVCR_VER >= 110 + if(!locinfo->lc_name[cat] || !sname) return TRUE; + return wcscmp(sname, locinfo->lc_name[cat]) != 0 || cp!=locinfo->lc_id[cat].wCodePage; +#else + lcid = sname ? LocaleNameToLCID(sname, 0) : 0; return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage; +#endif } -static __lc_time_data* create_time_data(LCID lcid) +static __lc_time_data* create_time_data(WCHAR *sname) { static const DWORD time_data[] = { LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, @@ -1189,6 +1210,7 @@ static __lc_time_data* create_time_data(LCID lcid) __lc_time_data *cur; int i, ret, size; + LCID lcid = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS); size = sizeof(__lc_time_data); for(i=0; i= 100 - ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0); + ret = GetLocaleInfoEx(sname, time_data[i], NULL, 0); if(!ret) return NULL; size += ret*sizeof(wchar_t); #endif } #if _MSVCR_VER >= 110 - size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(wchar_t); + size += wcslen(sname)*sizeof(wchar_t); #endif cur = malloc(size); @@ -1220,13 +1242,13 @@ static __lc_time_data* create_time_data(LCID lcid) #if _MSVCR_VER == 0 || _MSVCR_VER >= 100 for(i=0; iwstr.wstr[i] = (wchar_t*)&cur->data[ret]; - ret += GetLocaleInfoW(lcid, time_data[i], + ret += GetLocaleInfoEx(sname, time_data[i], (wchar_t*)&cur->data[ret], size-ret)*sizeof(wchar_t); } #endif #if _MSVCR_VER >= 110 cur->locname = (wchar_t*)&cur->data[ret]; - LCIDToLocaleName(lcid, (wchar_t*)&cur->data[ret], (size-ret)/sizeof(wchar_t), 0); + wcsncpy((wchar_t *) &cur->data[ret], sname, size-ret); #else cur->lcid = lcid; #endif @@ -1245,16 +1267,14 @@ static pthreadlocinfo create_locinfo(int category, static const char numeric[] = "NUMERIC="; static const char time[] = "TIME="; - pthreadlocinfo locinfo; - LCID lcid[6] = { 0 }; + pthreadlocinfo locinfo = NULL; unsigned short cp[6] = { 0 }; const char *locale_name[6] = { 0 }; + WCHAR *locale_sname[6] = { 0 }; int val, locale_len[6] = { 0 }; char buf[256]; - BOOL sname; -#if _MSVCR_VER >= 100 + BOOL sname_match; wchar_t wbuf[256]; -#endif int i; TRACE("(%d %s)\n", category, locale); @@ -1263,9 +1283,10 @@ static pthreadlocinfo create_locinfo(int category, return NULL; if(locale[0]=='C' && !locale[1]) { - lcid[0] = 0; + locale_sname[0] = NULL; cp[0] = CP_ACP; } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') { + LCID lcid; const char *p; while(1) { @@ -1286,30 +1307,39 @@ static pthreadlocinfo create_locinfo(int category, i = LC_TIME; locale += sizeof(time)-1; } else - return NULL; + goto fail; p = strchr(locale, ';'); if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) { - lcid[i] = 0; + lcid = 0; + locale_sname[i] = NULL; cp[i] = CP_ACP; } else if(p) { memcpy(buf, locale, p-locale); buf[p-locale] = '\0'; - lcid[i] = locale_to_LCID(buf, &cp[i], &sname); - if(sname) { + lcid = locale_to_LCID(buf, &cp[i], &sname_match); + if(sname_match) { locale_name[i] = locale; locale_len[i] = p-locale; } } else { - lcid[i] = locale_to_LCID(locale, &cp[i], &sname); - if(sname) { + lcid = locale_to_LCID(locale, &cp[i], &sname_match); + if(sname_match) { locale_name[i] = locale; locale_len[i] = strlen(locale); } } - if(lcid[i] == -1) - return NULL; + if(lcid == -1) + goto fail; + + if(lcid) { + int sname_size = LCIDToLocaleName(lcid, NULL, 0, LCID_CONVERSION_FLAGS); + locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); + if(!locale_sname[i]) + goto fail; + LCIDToLocaleName(lcid, locale_sname[i], sname_size, LCID_CONVERSION_FLAGS); + } if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_') break; @@ -1317,16 +1347,26 @@ static pthreadlocinfo create_locinfo(int category, locale = p+1; } } else { - lcid[0] = locale_to_LCID(locale, &cp[0], &sname); - if(lcid[0] == -1) + LCID lcid = locale_to_LCID(locale, &cp[0], &sname_match); + if(lcid == -1) return NULL; - if(sname) { + if(lcid) { + int sname_size = LCIDToLocaleName(lcid, NULL, 0, LCID_CONVERSION_FLAGS); + locale_sname[0] = malloc(sname_size * sizeof(WCHAR)); + if(!locale_sname[0]) + return NULL; + LCIDToLocaleName(lcid, locale_sname[0], sname_size, LCID_CONVERSION_FLAGS); + } + if(sname_match) { locale_name[0] = locale; locale_len[0] = strlen(locale); } for(i=1; i<6; i++) { - lcid[i] = lcid[0]; + locale_sname[i] = wcsdup(locale_sname[0]); + if(!locale_sname[i]) + goto fail; + cp[i] = cp[0]; locale_name[i] = locale_name[0]; locale_len[i] = locale_len[0]; @@ -1336,18 +1376,51 @@ static pthreadlocinfo create_locinfo(int category, for(i=1; i<6; i++) { #if _MSVCR_VER < 140 if(i==LC_CTYPE && cp[i]==CP_UTF8) { +#if _MSVCR_VER >= 110 + if(old_locinfo) { + locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); + if (old_locinfo->lc_name[i] && !locale_sname[i]) + goto fail; + } +#else + int sname_size; + if(old_locinfo && old_locinfo->lc_handle[i]) { + sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0); + locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); + if(!locale_sname[i]) + goto fail; + LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0); + } else { + locale_sname[i] = NULL; + } +#endif + locale_name[i] = NULL; locale_len[i] = 0; - lcid[i] = old_locinfo ? old_locinfo->lc_handle[i] : 0; cp[i] = old_locinfo ? old_locinfo->lc_id[i].wCodePage : 0; } #endif if(category!=LC_ALL && category!=i) { if(old_locinfo) { - lcid[i] = old_locinfo->lc_handle[i]; +#if _MSVCR_VER >= 110 + locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); + if(old_locinfo->lc_name[i] && !locale_sname[i]) + goto fail; +#else + int sname_size; + if(old_locinfo->lc_handle[i]) { + sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0); + locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); + if(!locale_sname[i]) + goto fail; + LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0); + } else { + locale_sname[i] = NULL; + } +#endif cp[i] = old_locinfo->lc_id[i].wCodePage; } else { - lcid[i] = 0; + locale_sname[i] = NULL; cp[i] = 0; } } @@ -1355,7 +1428,7 @@ static pthreadlocinfo create_locinfo(int category, locinfo = malloc(sizeof(threadlocinfo)); if(!locinfo) - return NULL; + goto fail; memset(locinfo, 0, sizeof(threadlocinfo)); locinfo->refcount = 1; @@ -1363,38 +1436,34 @@ static pthreadlocinfo create_locinfo(int category, if(locale_name[LC_COLLATE] && !init_category_name(locale_name[LC_COLLATE], locale_len[LC_COLLATE], locinfo, LC_COLLATE)) { - free_locinfo(locinfo); - return NULL; + goto fail; } if(!category_needs_update(LC_COLLATE, old_locinfo, - lcid[LC_COLLATE], cp[LC_COLLATE])) { + locale_sname[LC_COLLATE], cp[LC_COLLATE])) { copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE); locinfo->lc_collate_cp = old_locinfo->lc_collate_cp; - } else if(lcid[LC_COLLATE]) { - if(!update_threadlocinfo_category(lcid[LC_COLLATE], + } else if(locale_sname[LC_COLLATE]) { + if(!update_threadlocinfo_category(locale_sname[LC_COLLATE], cp[LC_COLLATE], locinfo, LC_COLLATE)) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage; } else { if(!init_category_name("C", 1, locinfo, LC_COLLATE)) { - free_locinfo(locinfo); - return NULL; + goto fail; } } if(locale_name[LC_CTYPE] && !init_category_name(locale_name[LC_CTYPE], locale_len[LC_CTYPE], locinfo, LC_CTYPE)) { - free_locinfo(locinfo); - return NULL; + goto fail; } if(!category_needs_update(LC_CTYPE, old_locinfo, - lcid[LC_CTYPE], cp[LC_CTYPE])) { + locale_sname[LC_CTYPE], cp[LC_CTYPE])) { copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE); locinfo->lc_codepage = old_locinfo->lc_codepage; locinfo->lc_clike = old_locinfo->lc_clike; @@ -1406,28 +1475,25 @@ static pthreadlocinfo create_locinfo(int category, locinfo->pcumap = old_locinfo->pcumap; if(locinfo->ctype1_refcount) InterlockedIncrement((LONG *)locinfo->ctype1_refcount); - } else if(lcid[LC_CTYPE]) { + } else if(locale_sname[LC_CTYPE]) { CPINFO cp_info; int j; - if(!update_threadlocinfo_category(lcid[LC_CTYPE], + if(!update_threadlocinfo_category(locale_sname[LC_CTYPE], cp[LC_CTYPE], locinfo, LC_CTYPE)) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage; locinfo->lc_clike = 1; if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->mb_cur_max = cp_info.MaxCharSize; locinfo->ctype1_refcount = malloc(sizeof(int)); if(!locinfo->ctype1_refcount) { - free_locinfo(locinfo); - return NULL; + goto fail; } *locinfo->ctype1_refcount = 1; @@ -1435,8 +1501,7 @@ static pthreadlocinfo create_locinfo(int category, locinfo->pclmap = malloc(sizeof(char[256])); locinfo->pcumap = malloc(sizeof(char[256])); if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->ctype1[0] = 0; @@ -1449,7 +1514,7 @@ static pthreadlocinfo create_locinfo(int category, /* builtin GetStringTypeA doesn't set output to 0 on invalid input */ locinfo->ctype1[i] = 0; - GetStringTypeA(lcid[LC_CTYPE], CT_CTYPE1, buf, + GetStringTypeA(locinfo->lc_handle[LC_CTYPE], CT_CTYPE1, buf, 1, locinfo->ctype1+i); } @@ -1464,9 +1529,9 @@ static pthreadlocinfo create_locinfo(int category, buf[i] = i; } - LCMapStringA(lcid[LC_CTYPE], LCMAP_LOWERCASE, buf, 256, + LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_LOWERCASE, buf, 256, (char*)locinfo->pclmap, 256); - LCMapStringA(lcid[LC_CTYPE], LCMAP_UPPERCASE, buf, 256, + LCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_UPPERCASE, buf, 256, (char*)locinfo->pcumap, 256); } else { locinfo->lc_clike = 1; @@ -1475,20 +1540,19 @@ static pthreadlocinfo create_locinfo(int category, locinfo->pclmap = cloc_clmap; locinfo->pcumap = cloc_cumap; if(!init_category_name("C", 1, locinfo, LC_CTYPE)) { - free_locinfo(locinfo); - return NULL; + goto fail; } } if(!category_needs_update(LC_MONETARY, old_locinfo, - lcid[LC_MONETARY], cp[LC_MONETARY]) && + locale_sname[LC_MONETARY], cp[LC_MONETARY]) && !category_needs_update(LC_NUMERIC, old_locinfo, - lcid[LC_NUMERIC], cp[LC_NUMERIC])) { + locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) { locinfo->lconv = old_locinfo->lconv; locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount; if(locinfo->lconv_intl_refcount) InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount); - } else if(lcid[LC_MONETARY] || lcid[LC_NUMERIC]) { + } else if(locale_sname[LC_MONETARY] || locale_sname[LC_NUMERIC]) { locinfo->lconv = malloc(sizeof(struct lconv)); locinfo->lconv_intl_refcount = malloc(sizeof(int)); if(!locinfo->lconv || !locinfo->lconv_intl_refcount) { @@ -1496,8 +1560,7 @@ static pthreadlocinfo create_locinfo(int category, free(locinfo->lconv_intl_refcount); locinfo->lconv = NULL; locinfo->lconv_intl_refcount = NULL; - free_locinfo(locinfo); - return NULL; + goto fail; } memset(locinfo->lconv, 0, sizeof(struct lconv)); *locinfo->lconv_intl_refcount = 1; @@ -1508,12 +1571,11 @@ static pthreadlocinfo create_locinfo(int category, if(locale_name[LC_MONETARY] && !init_category_name(locale_name[LC_MONETARY], locale_len[LC_MONETARY], locinfo, LC_MONETARY)) { - free_locinfo(locinfo); - return NULL; + goto fail; } if(!category_needs_update(LC_MONETARY, old_locinfo, - lcid[LC_MONETARY], cp[LC_MONETARY])) { + locale_sname[LC_MONETARY], cp[LC_MONETARY])) { copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY); locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount; if(locinfo->lconv_mon_refcount) @@ -1543,59 +1605,58 @@ static pthreadlocinfo create_locinfo(int category, locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign; #endif } - } else if(lcid[LC_MONETARY]) { - if(!update_threadlocinfo_category(lcid[LC_MONETARY], + } else if(locale_sname[LC_MONETARY]) { + if(!update_threadlocinfo_category(locale_sname[LC_MONETARY], cp[LC_MONETARY], locinfo, LC_MONETARY)) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->lconv_mon_refcount = malloc(sizeof(int)); if(!locinfo->lconv_mon_refcount) { - free_locinfo(locinfo); - return NULL; + goto fail; } *locinfo->lconv_mon_refcount = 1; - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->int_curr_symbol = malloc(i))) - memcpy(locinfo->lconv->int_curr_symbol, buf, i); + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->int_curr_symbol, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SCURRENCY - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->currency_symbol = malloc(i))) - memcpy(locinfo->lconv->currency_symbol, buf, i); + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->currency_symbol, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->mon_decimal_point = malloc(i))) - memcpy(locinfo->lconv->mon_decimal_point, buf, i); + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_decimal_point, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->mon_thousands_sep = malloc(i))) - memcpy(locinfo->lconv->mon_thousands_sep, buf, i); + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_thousands_sep, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONGROUPING - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONGROUPING + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, 256, NULL, NULL); if(i>1) i = i/2 + (buf[i-2]=='0'?0:1); if(i && (locinfo->lconv->mon_grouping = malloc(i))) { @@ -1605,145 +1666,130 @@ static pthreadlocinfo create_locinfo(int category, if(buf[i] != '0') locinfo->lconv->mon_grouping[i/2+1] = 127; } else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->positive_sign = malloc(i))) - memcpy(locinfo->lconv->positive_sign, buf, i); + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->positive_sign, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->negative_sign = malloc(i))) - memcpy(locinfo->lconv->negative_sign, buf, i); + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->negative_sign, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IINTLCURRDIGITS + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IINTLCURRDIGITS |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->int_frac_digits = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_ICURRDIGITS + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_ICURRDIGITS |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->frac_digits = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSYMPRECEDES + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSYMPRECEDES |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->p_cs_precedes = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSEPBYSPACE + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSEPBYSPACE |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->p_sep_by_space = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSYMPRECEDES + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSYMPRECEDES |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->n_cs_precedes = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSEPBYSPACE + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSEPBYSPACE |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->n_sep_by_space = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_IPOSSIGNPOSN + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSIGNPOSN |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->p_sign_posn = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } - if(GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_INEGSIGNPOSN + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSIGNPOSN |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) locinfo->lconv->n_sign_posn = val; else { - free_locinfo(locinfo); - return NULL; + goto fail; } #if _MSVCR_VER >= 100 - i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SCURRENCY + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoW(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } #endif } else { @@ -1775,20 +1821,18 @@ static pthreadlocinfo create_locinfo(int category, } if(!init_category_name("C", 1, locinfo, LC_MONETARY)) { - free_locinfo(locinfo); - return NULL; + goto fail; } } if(locale_name[LC_NUMERIC] && !init_category_name(locale_name[LC_NUMERIC], locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) { - free_locinfo(locinfo); - return NULL; + goto fail; } if(!category_needs_update(LC_NUMERIC, old_locinfo, - lcid[LC_NUMERIC], cp[LC_NUMERIC])) { + locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) { copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC); locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount; if(locinfo->lconv_num_refcount) @@ -1802,41 +1846,40 @@ static pthreadlocinfo create_locinfo(int category, locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep; #endif } - } else if(lcid[LC_NUMERIC]) { - if(!update_threadlocinfo_category(lcid[LC_NUMERIC], + } else if(locale_sname[LC_NUMERIC]) { + if(!update_threadlocinfo_category(locale_sname[LC_NUMERIC], cp[LC_NUMERIC], locinfo, LC_NUMERIC)) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->lconv_num_refcount = malloc(sizeof(int)); if(!locinfo->lconv_num_refcount) { - free_locinfo(locinfo); - return NULL; + goto fail; } *locinfo->lconv_num_refcount = 1; - i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SDECIMAL - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->decimal_point = malloc(i))) - memcpy(locinfo->lconv->decimal_point, buf, i); + WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->decimal_point, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_STHOUSAND - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL); if(i && (locinfo->lconv->thousands_sep = malloc(i))) - memcpy(locinfo->lconv->thousands_sep, buf, i); + WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->thousands_sep, i, NULL, NULL); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SGROUPING - |LOCALE_NOUSEROVERRIDE, buf, 256); + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SGROUPING + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, buf, 256, NULL, NULL); if(i>1) i = i/2 + (buf[i-2]=='0'?0:1); if(i && (locinfo->lconv->grouping = malloc(i))) { @@ -1846,27 +1889,24 @@ static pthreadlocinfo create_locinfo(int category, if(buf[i] != '0') locinfo->lconv->grouping[i/2+1] = 127; } else { - free_locinfo(locinfo); - return NULL; + goto fail; } #if _MSVCR_VER >= 100 - i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_SDECIMAL + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } - i = GetLocaleInfoW(lcid[LC_NUMERIC], LOCALE_STHOUSAND + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND |LOCALE_NOUSEROVERRIDE, wbuf, 256); if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t)))) memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t)); else { - free_locinfo(locinfo); - return NULL; + goto fail; } #endif } else { @@ -1882,45 +1922,48 @@ static pthreadlocinfo create_locinfo(int category, } if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) { - free_locinfo(locinfo); - return NULL; + goto fail; } } if(locale_name[LC_TIME] && !init_category_name(locale_name[LC_TIME], locale_len[LC_TIME], locinfo, LC_TIME)) { - free_locinfo(locinfo); - return NULL; + goto fail; } if(!category_needs_update(LC_TIME, old_locinfo, - lcid[LC_TIME], cp[LC_TIME])) { + locale_sname[LC_TIME], cp[LC_TIME])) { copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME); locinfo->lc_time_curr = old_locinfo->lc_time_curr; InterlockedIncrement(&locinfo->lc_time_curr->refcount); - } else if(lcid[LC_TIME]) { - if(!update_threadlocinfo_category(lcid[LC_TIME], + } else if(locale_sname[LC_TIME]) { + if(!update_threadlocinfo_category(locale_sname[LC_TIME], cp[LC_TIME], locinfo, LC_TIME)) { - free_locinfo(locinfo); - return NULL; + goto fail; } - locinfo->lc_time_curr = create_time_data(lcid[LC_TIME]); + locinfo->lc_time_curr = create_time_data(locale_sname[LC_TIME]); if(!locinfo->lc_time_curr) { - free_locinfo(locinfo); - return NULL; + goto fail; } } else { if(!init_category_name("C", 1, locinfo, LC_TIME)) { - free_locinfo(locinfo); - return NULL; + goto fail; } locinfo->lc_time_curr = &cloc_time_data; InterlockedIncrement(&locinfo->lc_time_curr->refcount); } return locinfo; + +fail: + free_locinfo(locinfo); + + for (i = 0; i < LC_MAX; i++) + free(locale_sname[i]); + + return NULL; } /********************************************************************* From 8fb065c032a6b8a16db6acf447ad9c468bf3cf1a Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Wed, 7 Jun 2023 12:41:42 -0300 Subject: [PATCH 672/758] msvcrt: Convert locale_to_LCID to snames. (cherry picked from commit 45dd09d0cf63892a5518ab718e32aea450580cf8) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 95 ++++++++++++++++++++------------------------ dlls/msvcrt/mbcs.c | 3 +- dlls/msvcrt/msvcrt.h | 5 +-- 3 files changed, 48 insertions(+), 55 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 845de26155c..cfcd4410b52 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -321,28 +321,27 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) return CONTINUE_LOOKING; } -/* Internal: Find the LCID for a locale specification */ -LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname) +/* Internal: Find the sname for a locale specification */ +BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname, int sname_size) { thread_data_t *data = msvcrt_get_thread_data(); const char *cp, *region; BOOL is_sname = FALSE; DWORD locale_cp; - LCID lcid; if (!strcmp(locale, data->cached_locale)) { if (codepage) *codepage = data->cached_cp; if (sname) - *sname = data->cached_sname; - return data->cached_lcid; + wcsncpy(sname, data->cached_sname, sname_size); + return TRUE; } cp = strchr(locale, '.'); region = strchr(locale, '_'); if(!locale[0] || (cp == locale && !region)) { - lcid = GetUserDefaultLCID(); + GetUserDefaultLocaleName(sname, sname_size); } else { locale_search_t search; @@ -371,26 +370,26 @@ LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname) EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL); if (!search.match_flags) - return -1; + return FALSE; /* If we were given something that didn't match, fail */ if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE))) - return -1; + return FALSE; if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) - return -1; + return FALSE; - lcid = MAKELCID(search.found_lang_id, SORT_DEFAULT); + LCIDToLocaleName(search.found_lang_id, sname, sname_size, LOCALE_ALLOW_NEUTRAL_NAMES); is_sname = (search.match_flags & FOUND_SNAME) != 0; } /* Obtain code page */ if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) { - GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + GetLocaleInfoEx(sname, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR)); if (!locale_cp) locale_cp = GetACP(); } else if (!_strnicmp(cp, ".OCP", 4)) { - GetLocaleInfoW(lcid, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, + GetLocaleInfoEx(sname, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR)); #if _MSVCR_VER >= 140 } else if (!_strnicmp(cp, ".UTF-8", 6) @@ -401,24 +400,23 @@ LCID locale_to_LCID(const char *locale, unsigned short *codepage, BOOL *sname) locale_cp = atoi(cp + 1); } if (!IsValidCodePage(locale_cp)) - return -1; + return FALSE; if (!locale_cp) - return -1; + return FALSE; if (codepage) *codepage = locale_cp; - if (sname) - *sname = is_sname; + if (sname_match) + *sname_match = is_sname; if (strlen(locale) < sizeof(data->cached_locale)) { strcpy(data->cached_locale, locale); - data->cached_lcid = lcid; data->cached_cp = locale_cp; - data->cached_sname = is_sname; + wcscpy(data->cached_sname, sname); } - return lcid; + return TRUE; } static void copy_threadlocinfo_category(pthreadlocinfo locinfo, @@ -1286,7 +1284,6 @@ static pthreadlocinfo create_locinfo(int category, locale_sname[0] = NULL; cp[0] = CP_ACP; } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') { - LCID lcid; const char *p; while(1) { @@ -1311,34 +1308,31 @@ static pthreadlocinfo create_locinfo(int category, p = strchr(locale, ';'); if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) { - lcid = 0; locale_sname[i] = NULL; cp[i] = CP_ACP; - } else if(p) { - memcpy(buf, locale, p-locale); - buf[p-locale] = '\0'; - lcid = locale_to_LCID(buf, &cp[i], &sname_match); - if(sname_match) { - locale_name[i] = locale; - locale_len[i] = p-locale; - } } else { - lcid = locale_to_LCID(locale, &cp[i], &sname_match); - if(sname_match) { - locale_name[i] = locale; - locale_len[i] = strlen(locale); + BOOL locale_found = FALSE; + + if(p) { + memcpy(buf, locale, p-locale); + buf[p-locale] = '\0'; + locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + locale_sname[i] = wcsdup(wbuf); + if(sname_match) { + locale_name[i] = locale; + locale_len[i] = p-locale; + } + } else { + locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + locale_sname[i] = wcsdup(wbuf); + if(sname_match) { + locale_name[i] = locale; + locale_len[i] = strlen(locale); + } } - } - - if(lcid == -1) - goto fail; - if(lcid) { - int sname_size = LCIDToLocaleName(lcid, NULL, 0, LCID_CONVERSION_FLAGS); - locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); - if(!locale_sname[i]) + if(!locale_found || !locale_sname[i]) goto fail; - LCIDToLocaleName(lcid, locale_sname[i], sname_size, LCID_CONVERSION_FLAGS); } if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_') @@ -1347,16 +1341,15 @@ static pthreadlocinfo create_locinfo(int category, locale = p+1; } } else { - LCID lcid = locale_to_LCID(locale, &cp[0], &sname_match); - if(lcid == -1) + BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + + if(!locale_found) return NULL; - if(lcid) { - int sname_size = LCIDToLocaleName(lcid, NULL, 0, LCID_CONVERSION_FLAGS); - locale_sname[0] = malloc(sname_size * sizeof(WCHAR)); - if(!locale_sname[0]) - return NULL; - LCIDToLocaleName(lcid, locale_sname[0], sname_size, LCID_CONVERSION_FLAGS); - } + + locale_sname[0] = wcsdup(wbuf); + if(!locale_sname[0]) + return NULL; + if(sname_match) { locale_name[0] = locale; locale_len[0] = strlen(locale); diff --git a/dlls/msvcrt/mbcs.c b/dlls/msvcrt/mbcs.c index 16c5c378be7..8598bceb029 100644 --- a/dlls/msvcrt/mbcs.c +++ b/dlls/msvcrt/mbcs.c @@ -252,8 +252,9 @@ threadmbcinfo* create_mbcinfo(int cp, LCID lcid, threadmbcinfo *old_mbcinfo) } if(lcid == -1) { + WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; sprintf(bufA, ".%d", newcp); - mbcinfo->mblcid = locale_to_LCID(bufA, NULL, NULL); + mbcinfo->mblcid = locale_to_sname(bufA, NULL, NULL, wbuf, LOCALE_NAME_MAX_LENGTH) ? LocaleNameToLCID(wbuf, LOCALE_ALLOW_NEUTRAL_NAMES) : -1; } else { mbcinfo->mblcid = lcid; } diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 1d965ff8ffc..73c02cd03b4 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -160,8 +160,7 @@ struct __thread_data { int processing_throw; frame_info *frame_info_head; void *unk8[6]; - LCID cached_lcid; - BOOL cached_sname; + WCHAR cached_sname[LOCALE_NAME_MAX_LENGTH]; int unk9[2]; DWORD cached_cp; char cached_locale[131]; @@ -176,7 +175,7 @@ typedef struct __thread_data thread_data_t; extern thread_data_t *CDECL msvcrt_get_thread_data(void) DECLSPEC_HIDDEN; -LCID locale_to_LCID(const char*, unsigned short*, BOOL*) DECLSPEC_HIDDEN; +BOOL locale_to_sname(const char*, unsigned short*, BOOL*, WCHAR*, int) DECLSPEC_HIDDEN; extern _locale_t MSVCRT_locale DECLSPEC_HIDDEN; extern __lc_time_data cloc_time_data DECLSPEC_HIDDEN; extern unsigned int MSVCRT___lc_codepage; From 984b917778afe3deff46f6ea29d794d6bf280238 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Mon, 12 Jun 2023 16:51:02 -0300 Subject: [PATCH 673/758] msvcrt: Skip exhaustive locale search with valid snames. (cherry picked from commit 72c3b8f3a0a9020ce3f2667fb63b94d45828c2dd) CW-Bug-Id: #19646 --- dlls/msvcr110/tests/msvcr110.c | 4 ++-- dlls/msvcr120/tests/msvcr120.c | 4 ++-- dlls/msvcrt/locale.c | 31 ++++++++++++++++++++++--------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/dlls/msvcr110/tests/msvcr110.c b/dlls/msvcr110/tests/msvcr110.c index 60d876e3bfc..35ba370bb49 100644 --- a/dlls/msvcr110/tests/msvcr110.c +++ b/dlls/msvcr110/tests/msvcr110.c @@ -153,14 +153,14 @@ static void test_setlocale(void) ok(!ret, "setlocale(en-us.1250) succeeded (%s)\n", ret); ret = p_setlocale(LC_ALL, "zh-Hans"); - todo_wine ok((ret != NULL + ok((ret != NULL || broken(ret == NULL)), /* Vista */ "expected success, but got NULL\n"); if (ret) ok(!strcmp(ret, "zh-Hans"), "setlocale zh-Hans failed\n"); ret = p_setlocale(LC_ALL, "zh-Hant"); - todo_wine ok((ret != NULL + ok((ret != NULL || broken(ret == NULL)), /* Vista */ "expected success, but got NULL\n"); if (ret) diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index b8950288afe..f45152078ed 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -598,11 +598,11 @@ static void test____lc_locale_name_func(void) p_setlocale(LC_ALL, "zh-Hans"); lc_names = p____lc_locale_name_func(); - todo_wine ok(!lstrcmpW(lc_names[1], L"zh-Hans"), "lc_names[1] expected zh-Hans got %s\n", wine_dbgstr_w(lc_names[1])); + ok(!lstrcmpW(lc_names[1], L"zh-Hans"), "lc_names[1] expected zh-Hans got %s\n", wine_dbgstr_w(lc_names[1])); p_setlocale(LC_ALL, "zh-Hant"); lc_names = p____lc_locale_name_func(); - todo_wine ok(!lstrcmpW(lc_names[1], L"zh-Hant"), "lc_names[1] expected zh-Hant got %s\n", wine_dbgstr_w(lc_names[1])); + ok(!lstrcmpW(lc_names[1], L"zh-Hant"), "lc_names[1] expected zh-Hant got %s\n", wine_dbgstr_w(lc_names[1])); p_setlocale(LC_ALL, "C"); lc_names = p____lc_locale_name_func(); diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index cfcd4410b52..12c9bba506c 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -343,6 +343,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if(!locale[0] || (cp == locale && !region)) { GetUserDefaultLocaleName(sname, sname_size); } else { + WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; locale_search_t search; memset(&search, 0, sizeof(locale_search_t)); @@ -364,21 +365,33 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if(!cp && !region) { remap_synonym(search.search_language); +#if _MSVCR_VER >= 110 search.allow_sname = TRUE; +#endif + } + + MultiByteToWideChar(CP_ACP, 0, search.search_language, -1, wbuf, LOCALE_NAME_MAX_LENGTH); + if (search.allow_sname && IsValidLocaleName(wbuf)) + { + search.match_flags = FOUND_SNAME; + wcsncpy(sname, wbuf, sname_size); } + else + { + EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL); - EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL); + if (!search.match_flags) + return FALSE; - if (!search.match_flags) - return FALSE; + /* If we were given something that didn't match, fail */ + if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE))) + return FALSE; + if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) + return FALSE; - /* If we were given something that didn't match, fail */ - if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE))) - return FALSE; - if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) - return FALSE; + LCIDToLocaleName(search.found_lang_id, sname, sname_size, LOCALE_ALLOW_NEUTRAL_NAMES); + } - LCIDToLocaleName(search.found_lang_id, sname, sname_size, LOCALE_ALLOW_NEUTRAL_NAMES); is_sname = (search.match_flags & FOUND_SNAME) != 0; } From a5d0cecac6310ce794b9fbb7c07af865c50ac19f Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Wed, 12 Jul 2023 01:47:43 -0300 Subject: [PATCH 674/758] msvcrt: Simplify set_lc_locale_name. (cherry picked from commit d71e93949869b215cacf185afbabd560f058694d) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 12c9bba506c..89118535494 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -471,32 +471,20 @@ static BOOL init_category_name(const char *name, int len, } #if _MSVCR_VER >= 110 -static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat) +static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname) { - LCID lcid = locinfo->lc_handle[cat]; - WCHAR buf[100]; - int len; - locinfo->lc_category[cat].wrefcount = malloc(sizeof(int)); if(!locinfo->lc_category[cat].wrefcount) return FALSE; *locinfo->lc_category[cat].wrefcount = 1; - len = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME - |LOCALE_NOUSEROVERRIDE, buf, 100); - if(!len) return FALSE; - - if(LocaleNameToLCID(buf, LOCALE_ALLOW_NEUTRAL_NAMES) != lcid) - len = LCIDToLocaleName(lcid, buf, 100, LOCALE_ALLOW_NEUTRAL_NAMES); - - if(!len || !(locinfo->lc_name[cat] = malloc(len*sizeof(wchar_t)))) + if(!(locinfo->lc_name[cat] = wcsdup(sname))) return FALSE; - memcpy(locinfo->lc_name[cat], buf, len*sizeof(wchar_t)); return TRUE; } #else -static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat) +static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname) { return TRUE; } @@ -531,7 +519,7 @@ static BOOL update_threadlocinfo_category(WCHAR *sname, unsigned short cp, locinfo->lc_handle[category] = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS); - set_lc_locale_name(locinfo, category); + set_lc_locale_name(locinfo, category, sname); if(!locinfo->lc_category[category].locale) { char buf[256]; From 6571ef71bf0e18a28565144e9ccf22fbc1c6e924 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Thu, 8 Jun 2023 21:43:14 -0300 Subject: [PATCH 675/758] msvcrt: Remap synonyms to snames. Gives us more control over what we map to which is required due to changes in Chinese locales. (cherry picked from commit ec7816a42a010443110abef2bf203707623535ec) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 98 ++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 89118535494..7e759ddc6c6 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -172,43 +172,54 @@ static struct lconv cloc_lconv = /* Friendly country strings & language names abbreviations. */ static const char * const _country_synonyms[] = { - "american", "enu", - "american english", "enu", - "american-english", "enu", - "english-american", "enu", - "english-us", "enu", - "english-usa", "enu", - "us", "enu", - "usa", "enu", - "australian", "ena", - "english-aus", "ena", - "belgian", "nlb", - "french-belgian", "frb", - "canadian", "enc", - "english-can", "enc", - "french-canadian", "frc", - "chinese", "chs", - "chinese-simplified", "chs", - "chinese-traditional", "cht", - "dutch-belgian", "nlb", - "english-nz", "enz", - "uk", "eng", - "english-uk", "eng", - "french-swiss", "frs", - "swiss", "des", - "german-swiss", "des", - "italian-swiss", "its", - "german-austrian", "dea", - "portuguese", "ptb", - "portuguese-brazil", "ptb", - "spanish-mexican", "esm", - "norwegian-bokmal", "nor", - "norwegian-nynorsk", "non", - "spanish-modern", "esn" + "american", "en", + "american english", "en-US", + "american-english", "en-US", + "english-american", "en-US", + "english-us", "en-US", + "english-usa", "en-US", + "us", "en-US", + "usa", "en-US", + "australian", "en-AU", + "english-aus", "en-AU", + "belgian", "nl-BE", + "french-belgian", "fr-BE", + "canadian", "en-CA", + "english-can", "en-CA", + "french-canadian", "fr-CA", +#if _MSVCR_VER >= 110 + "chinese", "zh", + "chinese-simplified", "zh", + "chinese-traditional", "zh-HK", + "chs", "zh", + "cht", "zh-HK", +#else + "chinese", "zh-CN", + "chinese-simplified", "zh-CN", + "chinese-traditional", "zh-TW", + "chs", "zh-CN", + "cht", "zh-TW", +#endif + "dutch-belgian", "nl-BE", + "english-nz", "en-NZ", + "uk", "en-GB", + "english-uk", "en-GB", + "french-swiss", "fr-CH", + "swiss", "de-CH", + "german-swiss", "de-CH", + "italian-swiss", "it-CH", + "german-austrian", "de-AT", + "portuguese", "pt-BR", + "portuguese-brazil", "pt-BR", + "spanish-mexican", "es-MX", + "norwegian-bokmal", "nb", + "norwegian-nynorsk", "nn-NO", + "spanish-modern", "es-ES" }; + /* INTERNAL: Map a synonym to an ISO code */ -static void remap_synonym(char *name) +static BOOL remap_synonym(char *name) { unsigned int i; for (i = 0; i < ARRAY_SIZE(_country_synonyms); i += 2) @@ -217,9 +228,11 @@ static void remap_synonym(char *name) { TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]); strcpy(name, _country_synonyms[i+1]); - return; + return TRUE; } } + + return FALSE; } /* Note: Flags are weighted in order of matching importance */ @@ -270,7 +283,6 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) if (lcid == LOCALE_CUSTOM_UNSPECIFIED) return CONTINUE_LOOKING; -#if _MSVCR_VER >= 110 if (res->allow_sname && compare_info(lcid,LOCALE_SNAME,buff,res->search_language, TRUE)) { TRACE(":Found locale: %s->%s\n", res->search_language, buff); @@ -278,7 +290,6 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) res->found_lang_id = LANGIDFROMLCID(lcid); return STOP_LOOKING; } -#endif /* Check Language */ if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || @@ -345,6 +356,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m } else { WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; locale_search_t search; + BOOL remapped = FALSE; memset(&search, 0, sizeof(locale_search_t)); lstrcpynA(search.search_language, locale, MAX_ELEM_LEN); @@ -362,13 +374,17 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m search.search_language[cp-locale] = '\0'; } - if(!cp && !region) + if ((remapped = remap_synonym(search.search_language))) { - remap_synonym(search.search_language); + search.allow_sname = TRUE; + } + #if _MSVCR_VER >= 110 + if(!cp && !region) + { search.allow_sname = TRUE; -#endif } +#endif MultiByteToWideChar(CP_ACP, 0, search.search_language, -1, wbuf, LOCALE_NAME_MAX_LENGTH); if (search.allow_sname && IsValidLocaleName(wbuf)) @@ -392,7 +408,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m LCIDToLocaleName(search.found_lang_id, sname, sname_size, LOCALE_ALLOW_NEUTRAL_NAMES); } - is_sname = (search.match_flags & FOUND_SNAME) != 0; + is_sname = !remapped && (search.match_flags & FOUND_SNAME) != 0; } /* Obtain code page */ From a349fc174abab4102628323bd3446aee75f178f9 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Tue, 6 Jun 2023 20:12:08 -0300 Subject: [PATCH 676/758] msvcrt: Use GetLocaleInfoEx to compare locale info. GetLocaleInfoA doesn't return the proper sname for neutral LCIDs. (cherry picked from commit 8f663f3d5eb0ec6eb7dbb6816294df287c640487) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 73 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 7e759ddc6c6..9fa6ba76143 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -241,11 +241,11 @@ static BOOL remap_synonym(char *name) #define FOUND_COUNTRY 0x1 typedef struct { - char search_language[MAX_ELEM_LEN]; - char search_country[MAX_ELEM_LEN]; + WCHAR search_language[MAX_ELEM_LEN]; + WCHAR search_country[MAX_ELEM_LEN]; + WCHAR found_lang_sname[LOCALE_NAME_MAX_LENGTH]; DWORD found_codepage; unsigned int match_flags; - LANGID found_lang_id; BOOL allow_sname; } locale_search_t; @@ -253,50 +253,48 @@ typedef struct { #define STOP_LOOKING FALSE /* INTERNAL: Get and compare locale info with a given string */ -static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp, BOOL exact) +static int compare_info(WCHAR *name, DWORD flags, WCHAR *buff, const WCHAR *cmp, BOOL exact) { int len; if(!cmp[0]) - return 0; + return 0; buff[0] = 0; - GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN); + GetLocaleInfoEx(name, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN); if (!buff[0]) return 0; /* Partial matches are only allowed on language/country names */ - len = strlen(cmp); + len = wcslen(cmp); + if(exact || len<=3) - return !_stricmp(cmp, buff); + return !_wcsicmp(cmp, buff); else - return !_strnicmp(cmp, buff, len); + return !_wcsnicmp(cmp, buff, len); } static BOOL CALLBACK find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) { locale_search_t *res = (locale_search_t *)lParam; - const LCID lcid = LocaleNameToLCID( name, LCID_CONVERSION_FLAGS ); - char buff[MAX_ELEM_LEN]; + WCHAR buff[MAX_ELEM_LEN]; unsigned int flags = 0; - if (lcid == LOCALE_CUSTOM_UNSPECIFIED) return CONTINUE_LOOKING; - - if (res->allow_sname && compare_info(lcid,LOCALE_SNAME,buff,res->search_language, TRUE)) + if (res->allow_sname && compare_info(name,LOCALE_SNAME,buff,res->search_language, TRUE)) { - TRACE(":Found locale: %s->%s\n", res->search_language, buff); + TRACE(":Found locale: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff)); res->match_flags = FOUND_SNAME; - res->found_lang_id = LANGIDFROMLCID(lcid); + wcscpy(res->found_lang_sname, name); return STOP_LOOKING; } /* Check Language */ - if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || - compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) || - compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE)) + if (compare_info(name,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || + compare_info(name,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) || + compare_info(name,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE)) { - TRACE(":Found language: %s->%s\n", res->search_language, buff); + TRACE(":Found language: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff)); flags |= FOUND_LANGUAGE; } else if (res->match_flags & FOUND_LANGUAGE) @@ -305,11 +303,11 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) } /* Check Country */ - if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) || - compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) || - compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE)) + if (compare_info(name,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) || + compare_info(name,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) || + compare_info(name,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE)) { - TRACE("Found country:%s->%s\n", res->search_country, buff); + TRACE("Found country:%s->%s\n", wine_dbgstr_w(res->search_country), wine_dbgstr_w(buff)); flags |= FOUND_COUNTRY; } else if (!flags && (res->match_flags & FOUND_COUNTRY)) @@ -321,7 +319,7 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) { /* Found a better match than previously */ res->match_flags = flags; - res->found_lang_id = LANGIDFROMLCID(lcid); + wcscpy(res->found_lang_sname, name); } if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) == (FOUND_LANGUAGE | FOUND_COUNTRY)) @@ -354,27 +352,27 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if(!locale[0] || (cp == locale && !region)) { GetUserDefaultLocaleName(sname, sname_size); } else { - WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; + char search_language_buf[MAX_ELEM_LEN] = { 0 }, search_country_buf[MAX_ELEM_LEN] = { 0 }; locale_search_t search; BOOL remapped = FALSE; memset(&search, 0, sizeof(locale_search_t)); - lstrcpynA(search.search_language, locale, MAX_ELEM_LEN); + lstrcpynA(search_language_buf, locale, MAX_ELEM_LEN); if(region) { - lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN); + lstrcpynA(search_country_buf, region+1, MAX_ELEM_LEN); if(region-locale < MAX_ELEM_LEN) - search.search_language[region-locale] = '\0'; + search_language_buf[region-locale] = '\0'; } else - search.search_country[0] = '\0'; + search_country_buf[0] = '\0'; if(cp) { if(region && cp-region-1 Date: Wed, 19 Jul 2023 22:17:25 +0100 Subject: [PATCH 677/758] msvcrt: Fix out-of-bound access in create_locinfo. Fixes regression introduced by 24a2b625545f1875b5c3177f2b9. Signed-off-by: Yuxuan Shui (cherry picked from commit f66c8972129e81707292a210ef4989a9ad57da89) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 9fa6ba76143..10682990bfc 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -1226,7 +1226,7 @@ static __lc_time_data* create_time_data(WCHAR *sname) int i, ret, size; LCID lcid = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS); - size = sizeof(__lc_time_data); + size = 0; for(i=0; i= 110 - size += wcslen(sname)*sizeof(wchar_t); + size += (wcslen(sname) + 1) * sizeof(wchar_t); #endif - cur = malloc(size); + cur = malloc(FIELD_OFFSET(__lc_time_data, data[size])); if(!cur) return NULL; @@ -1256,13 +1256,13 @@ static __lc_time_data* create_time_data(WCHAR *sname) #if _MSVCR_VER == 0 || _MSVCR_VER >= 100 for(i=0; iwstr.wstr[i] = (wchar_t*)&cur->data[ret]; - ret += GetLocaleInfoEx(sname, time_data[i], - (wchar_t*)&cur->data[ret], size-ret)*sizeof(wchar_t); + ret += GetLocaleInfoEx(sname, time_data[i], (wchar_t*)&cur->data[ret], + (size - ret) / sizeof(wchar_t)) * sizeof(wchar_t); } #endif #if _MSVCR_VER >= 110 cur->locname = (wchar_t*)&cur->data[ret]; - wcsncpy((wchar_t *) &cur->data[ret], sname, size-ret); + wcscpy((wchar_t*)&cur->data[ret], sname); #else cur->lcid = lcid; #endif From e65a91a63f9edf764fae8ff8abb14065c263cbcc Mon Sep 17 00:00:00 2001 From: Piotr Caban Date: Fri, 21 Jul 2023 16:46:41 +0200 Subject: [PATCH 678/758] msvcrt: Pass correct buffer to locale_to_sname helper in create_locinfo. (cherry picked from commit 092e68b2e0654affb8a13aa5f5222315726e6278) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 10682990bfc..7e94d7bf2a8 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -1339,7 +1339,7 @@ static pthreadlocinfo create_locinfo(int category, locale_len[i] = p-locale; } } else { - locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); locale_sname[i] = wcsdup(wbuf); if(sname_match) { locale_name[i] = locale; From b4601fbb0c595ff65762cd2e47d667942a3185b8 Mon Sep 17 00:00:00 2001 From: Piotr Caban Date: Fri, 21 Jul 2023 16:48:13 +0200 Subject: [PATCH 679/758] msvcrt: Improve locale_to_sname error handling. (cherry picked from commit f93f31dec58affa33d770a163490e760ed9991ee) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 7e94d7bf2a8..854ec6e7158 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -1333,22 +1333,16 @@ static pthreadlocinfo create_locinfo(int category, memcpy(buf, locale, p-locale); buf[p-locale] = '\0'; locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); - locale_sname[i] = wcsdup(wbuf); - if(sname_match) { - locale_name[i] = locale; - locale_len[i] = p-locale; - } } else { locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); - locale_sname[i] = wcsdup(wbuf); - if(sname_match) { - locale_name[i] = locale; - locale_len[i] = strlen(locale); - } } - if(!locale_found || !locale_sname[i]) + if(!locale_found || !(locale_sname[i] = wcsdup(wbuf))) goto fail; + if(sname_match) { + locale_name[i] = locale; + locale_len[i] = p ? p-locale : strlen(locale); + } } if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_') From 5c67a2821c7acaecf452557aad309e25f758c4b9 Mon Sep 17 00:00:00 2001 From: Piotr Caban Date: Fri, 21 Jul 2023 17:44:02 +0200 Subject: [PATCH 680/758] msvcrt: Set sname_match in locale_to_sname when returning cached result. (cherry picked from commit 4a4f138fe9542e1bee240f3b98a3ab91afa842e2) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 6 ++++-- dlls/msvcrt/msvcrt.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 854ec6e7158..153c4fb1c7d 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -341,8 +341,9 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if (!strcmp(locale, data->cached_locale)) { if (codepage) *codepage = data->cached_cp; - if (sname) - wcsncpy(sname, data->cached_sname, sname_size); + if (sname_match) + *sname_match = data->cached_sname_match; + wcsncpy(sname, data->cached_sname, sname_size); return TRUE; } @@ -441,6 +442,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if (strlen(locale) < sizeof(data->cached_locale)) { strcpy(data->cached_locale, locale); data->cached_cp = locale_cp; + data->cached_sname_match = is_sname; wcscpy(data->cached_sname, sname); } diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 73c02cd03b4..35c90fd90e3 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -160,6 +160,7 @@ struct __thread_data { int processing_throw; frame_info *frame_info_head; void *unk8[6]; + BOOL cached_sname_match; WCHAR cached_sname[LOCALE_NAME_MAX_LENGTH]; int unk9[2]; DWORD cached_cp; From 9e3652efd88ffd200fbd4b407a73b3e18565ed82 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Fri, 21 Jul 2023 17:08:12 -0300 Subject: [PATCH 681/758] msvcrt: Remove unused struct locale_search_t member. (cherry picked from commit 85317b51cb61adb829f57ef3ef16d87a7ba30f45) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index 153c4fb1c7d..b14aa73af05 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -244,7 +244,6 @@ typedef struct { WCHAR search_language[MAX_ELEM_LEN]; WCHAR search_country[MAX_ELEM_LEN]; WCHAR found_lang_sname[LOCALE_NAME_MAX_LENGTH]; - DWORD found_codepage; unsigned int match_flags; BOOL allow_sname; } locale_search_t; From 0a49e38778890ff4c4707652a30f52c010064fe2 Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Fri, 21 Jul 2023 19:20:21 -0300 Subject: [PATCH 682/758] msvcrt: Fix memory leak in create_locinfo. (cherry picked from commit 46596db7ed6a154dc1af98fc266ce2c47723fbaa) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index b14aa73af05..a24c0c16a48 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -1959,6 +1959,9 @@ static pthreadlocinfo create_locinfo(int category, InterlockedIncrement(&locinfo->lc_time_curr->refcount); } + for (i = 0; i < LC_MAX; i++) + free(locale_sname[i]); + return locinfo; fail: From 4cedb4adaa8046154244e46feca728dd2c2f7dec Mon Sep 17 00:00:00 2001 From: Victor Chiletto Date: Fri, 21 Jul 2023 19:51:11 -0300 Subject: [PATCH 683/758] msvcrt: Remove uses of wcsncpy from locale_to_sname. (cherry picked from commit fd2ddf8f2a90da51635de7a7653209783f10032d) CW-Bug-Id: #19646 --- dlls/msvcrt/locale.c | 20 +++++++++++--------- dlls/msvcrt/mbcs.c | 2 +- dlls/msvcrt/msvcrt.h | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c index a24c0c16a48..82083d66106 100644 --- a/dlls/msvcrt/locale.c +++ b/dlls/msvcrt/locale.c @@ -329,8 +329,10 @@ find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) return CONTINUE_LOOKING; } -/* Internal: Find the sname for a locale specification */ -BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname, int sname_size) +/* Internal: Find the sname for a locale specification. + * sname must be at least LOCALE_NAME_MAX_LENGTH characters long + */ +BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname) { thread_data_t *data = msvcrt_get_thread_data(); const char *cp, *region; @@ -342,7 +344,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m *codepage = data->cached_cp; if (sname_match) *sname_match = data->cached_sname_match; - wcsncpy(sname, data->cached_sname, sname_size); + wcscpy(sname, data->cached_sname); return TRUE; } @@ -350,7 +352,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m region = strchr(locale, '_'); if(!locale[0] || (cp == locale && !region)) { - GetUserDefaultLocaleName(sname, sname_size); + GetUserDefaultLocaleName(sname, LOCALE_NAME_MAX_LENGTH); } else { char search_language_buf[MAX_ELEM_LEN] = { 0 }, search_country_buf[MAX_ELEM_LEN] = { 0 }; locale_search_t search; @@ -388,7 +390,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if (search.allow_sname && IsValidLocaleName(search.search_language)) { search.match_flags = FOUND_SNAME; - wcsncpy(sname, search.search_language, sname_size); + wcscpy(sname, search.search_language); } else { @@ -404,7 +406,7 @@ BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_m if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) return FALSE; - wcsncpy(sname, search.found_lang_sname, sname_size); + wcscpy(sname, search.found_lang_sname); } is_sname = !remapped && (search.match_flags & FOUND_SNAME) != 0; @@ -1333,9 +1335,9 @@ static pthreadlocinfo create_locinfo(int category, if(p) { memcpy(buf, locale, p-locale); buf[p-locale] = '\0'; - locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf); } else { - locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf); } if(!locale_found || !(locale_sname[i] = wcsdup(wbuf))) @@ -1352,7 +1354,7 @@ static pthreadlocinfo create_locinfo(int category, locale = p+1; } } else { - BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf, LOCALE_NAME_MAX_LENGTH); + BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf); if(!locale_found) return NULL; diff --git a/dlls/msvcrt/mbcs.c b/dlls/msvcrt/mbcs.c index 8598bceb029..c8390288d4a 100644 --- a/dlls/msvcrt/mbcs.c +++ b/dlls/msvcrt/mbcs.c @@ -254,7 +254,7 @@ threadmbcinfo* create_mbcinfo(int cp, LCID lcid, threadmbcinfo *old_mbcinfo) if(lcid == -1) { WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; sprintf(bufA, ".%d", newcp); - mbcinfo->mblcid = locale_to_sname(bufA, NULL, NULL, wbuf, LOCALE_NAME_MAX_LENGTH) ? LocaleNameToLCID(wbuf, LOCALE_ALLOW_NEUTRAL_NAMES) : -1; + mbcinfo->mblcid = locale_to_sname(bufA, NULL, NULL, wbuf) ? LocaleNameToLCID(wbuf, LOCALE_ALLOW_NEUTRAL_NAMES) : -1; } else { mbcinfo->mblcid = lcid; } diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 35c90fd90e3..5fbd5497804 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -176,7 +176,7 @@ typedef struct __thread_data thread_data_t; extern thread_data_t *CDECL msvcrt_get_thread_data(void) DECLSPEC_HIDDEN; -BOOL locale_to_sname(const char*, unsigned short*, BOOL*, WCHAR*, int) DECLSPEC_HIDDEN; +BOOL locale_to_sname(const char*, unsigned short*, BOOL*, WCHAR*) DECLSPEC_HIDDEN; extern _locale_t MSVCRT_locale DECLSPEC_HIDDEN; extern __lc_time_data cloc_time_data DECLSPEC_HIDDEN; extern unsigned int MSVCRT___lc_codepage; From be1ed6ff7c1169d3c01e3d2e40ff2e4fda7dbb9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Jul 2023 09:22:31 +0200 Subject: [PATCH 684/758] Revert "ntoskrnl.exe: Enumerate devices in their invalidation order." This reverts commit 25af2062b760f01a11ef65ffb0b554b3f17f8f9c. CW-Bug-Id: #22500 --- dlls/ntoskrnl.exe/pnp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 3a2826a8f0a..7c77a9a7145 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -1126,9 +1126,7 @@ static DWORD CALLBACK device_enum_thread_proc(void *arg) while (!invalidated_devices_count) SleepConditionVariableCS( &invalidated_devices_cv, &invalidated_devices_cs, INFINITE ); - invalidated_devices_count--; - device = invalidated_devices[0]; - memmove( invalidated_devices, invalidated_devices + 1, invalidated_devices_count * sizeof(*invalidated_devices) ); + device = invalidated_devices[--invalidated_devices_count]; /* Don't hold the CS while enumerating the device. Tests show that * calling IoInvalidateDeviceRelations() from another thread shouldn't From 9a256c256db84faa6e1c458988d25adbfe6dd6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Jul 2023 09:23:05 +0200 Subject: [PATCH 685/758] Revert "ntoskrnl/tests: Fix a test failure." This reverts commit 03034f2c0ef23964891d60e5995d167a113f2d3e. CW-Bug-Id: #22500 --- dlls/ntoskrnl.exe/tests/driver_pnp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c index 9a965f584cd..824c6554693 100644 --- a/dlls/ntoskrnl.exe/tests/driver_pnp.c +++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c @@ -703,8 +703,7 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code) * handles to the device are closed (and the user-space thread is * currently blocked in this ioctl and won't close its handle * yet.) */ - todo_wine_if (remove_device_count) - ok(!remove_device_count, "Got %u remove events.\n", remove_device_count); + ok(!remove_device_count, "Got %u remove events.\n", remove_device_count); return STATUS_SUCCESS; } From 1839e258c8ba0e6d645f30ef28bb40e2705e80d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 27 Jul 2023 09:23:11 +0200 Subject: [PATCH 686/758] Revert "ntoskrnl: Enumerate child devices on a separate thread." This reverts commit 9fbdf2b43435caf62742d997bf8b3fb8752f606c. CW-Bug-Id: #22500 --- dlls/ntoskrnl.exe/ntoskrnl_private.h | 1 - dlls/ntoskrnl.exe/pnp.c | 40 +--------------------------- dlls/ntoskrnl.exe/tests/driver_pnp.c | 10 +++---- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/dlls/ntoskrnl.exe/ntoskrnl_private.h b/dlls/ntoskrnl.exe/ntoskrnl_private.h index ef1fa99057c..c736a9805a0 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl_private.h +++ b/dlls/ntoskrnl.exe/ntoskrnl_private.h @@ -22,7 +22,6 @@ #define __WINE_NTOSKRNL_PRIVATE_H #include -#include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" diff --git a/dlls/ntoskrnl.exe/pnp.c b/dlls/ntoskrnl.exe/pnp.c index 7c77a9a7145..3c3353b311f 100644 --- a/dlls/ntoskrnl.exe/pnp.c +++ b/dlls/ntoskrnl.exe/pnp.c @@ -38,12 +38,6 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); WINE_DEFAULT_DEBUG_CHANNEL(plugplay); -DECLARE_CRITICAL_SECTION(invalidated_devices_cs); -static CONDITION_VARIABLE invalidated_devices_cv = CONDITION_VARIABLE_INIT; - -static DEVICE_OBJECT **invalidated_devices; -static size_t invalidated_devices_count; - static inline const char *debugstr_propkey( const DEVPROPKEY *id ) { if (!id) return "(null)"; @@ -491,14 +485,8 @@ void WINAPI IoInvalidateDeviceRelations( DEVICE_OBJECT *device_object, DEVICE_RE switch (type) { case BusRelations: - EnterCriticalSection( &invalidated_devices_cs ); - invalidated_devices = realloc( invalidated_devices, - (invalidated_devices_count + 1) * sizeof(*invalidated_devices) ); - invalidated_devices[invalidated_devices_count++] = device_object; - LeaveCriticalSection( &invalidated_devices_cs ); - WakeConditionVariable( &invalidated_devices_cv ); + handle_bus_relations( device_object ); break; - default: FIXME("Unhandled relation %#x.\n", type); break; @@ -1115,30 +1103,6 @@ static NTSTATUS WINAPI pnp_manager_driver_entry( DRIVER_OBJECT *driver, UNICODE_ return STATUS_SUCCESS; } -static DWORD CALLBACK device_enum_thread_proc(void *arg) -{ - for (;;) - { - DEVICE_OBJECT *device; - - EnterCriticalSection( &invalidated_devices_cs ); - - while (!invalidated_devices_count) - SleepConditionVariableCS( &invalidated_devices_cv, &invalidated_devices_cs, INFINITE ); - - device = invalidated_devices[--invalidated_devices_count]; - - /* Don't hold the CS while enumerating the device. Tests show that - * calling IoInvalidateDeviceRelations() from another thread shouldn't - * block, even if this thread is blocked in an IRP handler. */ - LeaveCriticalSection( &invalidated_devices_cs ); - - handle_bus_relations( device ); - } - - return 0; -} - void pnp_manager_start(void) { static const WCHAR driver_nameW[] = {'\\','D','r','i','v','e','r','\\','P','n','p','M','a','n','a','g','e','r',0}; @@ -1162,8 +1126,6 @@ void pnp_manager_start(void) RpcStringFreeW( &binding_str ); if (err) ERR("RpcBindingFromStringBinding() failed, error %#lx\n", err); - - CreateThread( NULL, 0, device_enum_thread_proc, NULL, 0, NULL ); } void pnp_manager_stop_driver( struct wine_driver *driver ) diff --git a/dlls/ntoskrnl.exe/tests/driver_pnp.c b/dlls/ntoskrnl.exe/tests/driver_pnp.c index 824c6554693..f17781e3d13 100644 --- a/dlls/ntoskrnl.exe/tests/driver_pnp.c +++ b/dlls/ntoskrnl.exe/tests/driver_pnp.c @@ -275,11 +275,11 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp) device->power_state = PowerDeviceD0; status = ZwWaitForSingleObject(device->plug_event, TRUE, &wait_time); - ok(!status, "Failed to wait for child plug event, status %#lx.\n", status); + todo_wine ok(!status, "Failed to wait for child plug event, status %#lx.\n", status); status = ZwSetEvent(device->plug_event2, NULL); ok(!status, "Failed to set event, status %#lx.\n", status); status = ZwWaitForSingleObject(device->plug_event, TRUE, &wait_time); - ok(!status, "Failed to wait for child plug event, status %#lx.\n", status); + todo_wine ok(!status, "Failed to wait for child plug event, status %#lx.\n", status); ret = STATUS_SUCCESS; break; @@ -695,15 +695,15 @@ static NTSTATUS fdo_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG code) * for the other. */ status = ZwSetEvent(plug_event, NULL); - ok(!status, "Failed to set event, status %#lx.\n", status); + todo_wine ok(!status, "Failed to set event, status %#lx.\n", status); status = ZwWaitForSingleObject(plug_event2, TRUE, &wait_time); - ok(!status, "Failed to wait for child plug event, status %#lx.\n", status); + todo_wine ok(!status, "Failed to wait for child plug event, status %#lx.\n", status); ok(surprise_removal_count == 1, "Got %u surprise removal events.\n", surprise_removal_count); /* We shouldn't get IRP_MN_REMOVE_DEVICE until all user-space * handles to the device are closed (and the user-space thread is * currently blocked in this ioctl and won't close its handle * yet.) */ - ok(!remove_device_count, "Got %u remove events.\n", remove_device_count); + todo_wine ok(!remove_device_count, "Got %u remove events.\n", remove_device_count); return STATUS_SUCCESS; } From 0e1587f19a0ead04a6b158680f7cddc715cfe54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 31 Jul 2023 14:43:21 +0200 Subject: [PATCH 687/758] HACK: ntdll: Enable WINE_ENABLE_GST_LIVE_LATENCY for more games. CW-Bug-Id: #22090 --- dlls/ntdll/unix/loader.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index c4056e8934a..6c0fd8387cc 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2391,7 +2391,10 @@ static void hacks_init(void) setenv("LIBGL_ALWAYS_SOFTWARE", "1", 0); } - if (sgi && (!strcmp(sgi, "1364780") || !strcmp(sgi, "1952120") || !strcmp(sgi, "2154900"))) + if (sgi && (0 + || !strcmp(sgi, "1364780") || !strcmp(sgi, "1952120") || !strcmp(sgi, "2154900") /* Street Fighter 6 */ + || !strcmp(sgi, "1740720") /* Have a Nice Death */ + )) { ERR("HACK: setting WINE_ENABLE_GST_LIVE_LATENCY.\n"); setenv("WINE_ENABLE_GST_LIVE_LATENCY", "1", 0); From b4112af9cade1c9b576bd6d9ff3c0b25245b6b3f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 1 Aug 2023 09:09:37 -0600 Subject: [PATCH 688/758] ntdll: HACK: Enable WINE_SIMULATE_WRITECOPY for UplayWebCore. CW-Bug-Id: #22534 --- dlls/ntdll/unix/loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 6c0fd8387cc..0395883034a 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2358,6 +2358,7 @@ static void hacks_init(void) env_str = getenv("WINE_SIMULATE_WRITECOPY"); if (env_str) simulate_writecopy = atoi(env_str); + else if (main_argc > 1 && strstr(main_argv[1], "UplayWebCore.exe")) simulate_writecopy = TRUE; else if (sgi) simulate_writecopy = !strcmp(sgi, "1608730") /* Dawn of Corruption */ || !strcmp(sgi, "1680700") /* Purgo box */ || !strcmp(sgi, "2095300") /* Breakout 13 */ From b115788449f46704cc3f1b611ff9cd4df74f067e Mon Sep 17 00:00:00 2001 From: Michael Skorokhodov Date: Mon, 19 Jun 2023 21:26:02 +0300 Subject: [PATCH 689/758] win32u: Add DriverVersion string for GPUs to registry. Some applications (e.g. UE4) require the DriverVersion string in the registry. Signed-off-by: Mykhailo Skorokhodov (cherry picked from commit 973ed2579186385a97fc51520de75d175cc738a5) --- dlls/win32u/sysparams.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 055819d5aa0..6630250b667 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1224,6 +1224,8 @@ static void add_gpu( const struct gdi_gpu *gpu, void *param ) static const WCHAR ramdacW[] = {'I','n','t','e','r','g','r','a','t','e','d',' ','R','A','M','D','A','C',0}; static const WCHAR driver_dateW[] = {'D','r','i','v','e','r','D','a','t','e',0}; + static const WCHAR driver_versionW[] = + {'D','r','i','v','e','r','V','e','r','s','i','o','n',0}; TRACE( "%s %04X %04X %08X %02X\n", debugstr_w(gpu->name), gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id ); @@ -1368,6 +1370,31 @@ static void add_gpu( const struct gdi_gpu *gpu, void *param ) set_reg_value( hkey, chip_typeW, REG_BINARY, desc, size ); set_reg_value( hkey, dac_typeW, REG_BINARY, ramdacW, sizeof(ramdacW) ); + if (gpu->vendor_id && gpu->device_id) + { + /* The last seven digits are the driver number. */ + switch (gpu->vendor_id) + { + /* Intel */ + case 0x8086: + sprintf( buffer, "31.0.101.4576" ); + break; + /* AMD */ + case 0x1002: + sprintf( buffer, "31.0.14051.5006" ); + break; + /* Nvidia */ + case 0x10de: + sprintf( buffer, "31.0.15.3625" ); + break; + /* Default value for any other vendor. */ + default: + sprintf( buffer, "31.0.10.1000" ); + break; + } + set_reg_value( hkey, driver_versionW, REG_SZ, bufferW, asciiz_to_unicode( bufferW, buffer ) ); + } + NtClose( hkey ); link_device( ctx->gpuid, guid_devinterface_display_adapterW ); From 1362310e33fe02654b4b8b85b9ddf586673f65fe Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:24 -0600 Subject: [PATCH 690/758] Revert "fixup! winevulkan: Add support for signalling VkFence from virtualized VkQueues." This reverts commit db9d4549dd4fc28c157b8b8bb17a118ccab03dcd. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 845646e8343..e08ba2857cf 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -5426,7 +5426,7 @@ void wine_vkDestroyFence(VkDevice device_handle, VkFence fence_handle, const VkA if (fence->eventfd != -1) close(fence->eventfd); - device->funcs.p_vkDestroyFence(device->device, fence->fence, NULL); + device->funcs.p_vkDestroyFence(device->device, fence->fence, allocator); free(fence); } From 4121c83126b38fd70560abd35c7cf538ee984df5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:26 -0600 Subject: [PATCH 691/758] Revert "winevulkan: Only append VK_KHR_timeline_semaphore for Vk API version < 1.2." This reverts commit 145f580cf412cf6d875f0e2d16a014805e6d43ab. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e08ba2857cf..7b134c1f60e 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -522,8 +522,6 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de else if (!strcmp(extension_name, "VK_KHR_timeline_semaphore")) append_timeline = 0; } - if (append_timeline) - append_timeline = phys_dev->api_version < VK_API_VERSION_1_2 || phys_dev->instance->api_version < VK_API_VERSION_1_2; if (append_timeline) { append_timeline = 0; @@ -1750,25 +1748,6 @@ static void wine_vk_get_physical_device_external_semaphore_properties(struct win break; case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT: { - unsigned int i; - - if (phys_dev->api_version < VK_API_VERSION_1_2 || - phys_dev->instance->api_version < VK_API_VERSION_1_2) - { - for (i = 0; i < phys_dev->extension_count; i++) - { - if (!strcmp(phys_dev->extensions[i].extensionName, "VK_KHR_timeline_semaphore")) - break; - } - if (i == phys_dev->extension_count) - { - properties->exportFromImportedHandleTypes = 0; - properties->compatibleHandleTypes = 0; - properties->externalSemaphoreFeatures = 0; - return; - } - } - if ((p_semaphore_type_info = wine_vk_find_struct(&semaphore_info_dup, SEMAPHORE_TYPE_CREATE_INFO))) { p_semaphore_type_info->semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; From 2a7946c287ca7d957ddf0ed6f41103eea49aaf15 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:27 -0600 Subject: [PATCH 692/758] Revert "winevulkan: Rename for_each_d3d12_semaphore() into get_semaphore_by_index()." This reverts commit 4deb9cac53fbce0bac375d485aba6791d7037c7f. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 7b134c1f60e..fe8af265b75 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -4714,7 +4714,7 @@ struct queue_submit_unit }; /* Abstracts away the differences between VkSubmitInfo and VkSubmitInfo2. */ -static bool get_semaphore_by_index(struct queue_submit_unit *unit, bool signal, +static bool for_each_d3d12_semaphore(struct queue_submit_unit *unit, bool signal, struct wine_semaphore **semaphore_out, uint64_t **value_out, uint32_t counter) { VkTimelineSemaphoreSubmitInfo *timeline_values; @@ -4825,7 +4825,7 @@ static void *virtual_queue_worker(void *arg) goto free_submit_unit; /* Wait for all fences to have a pending signal */ - for (i = 0; get_semaphore_by_index(submit_unit, false, &sem, &timeline_value, i); i++) + for (i = 0; for_each_d3d12_semaphore(submit_unit, false, &sem, &timeline_value, i); i++) { if ((wait = submit_unit->waits[i])) { @@ -4840,7 +4840,7 @@ static void *virtual_queue_worker(void *arg) } } - for (i = 0; get_semaphore_by_index(submit_unit, true, &sem, &timeline_value, i); i++) + for (i = 0; for_each_d3d12_semaphore(submit_unit, true, &sem, &timeline_value, i); i++) { d3d12_semaphore_lock(sem); @@ -4855,7 +4855,7 @@ static void *virtual_queue_worker(void *arg) pthread_mutex_lock(&queue->signaller_mutex); - for (i = 0; get_semaphore_by_index(submit_unit, true, &sem, &timeline_value, i); i++) + for (i = 0; for_each_d3d12_semaphore(submit_unit, true, &sem, &timeline_value, i); i++) { if (vr == VK_SUCCESS) { From c6afdba3196b19492ce501c6f7a4e55262927fdd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:28 -0600 Subject: [PATCH 693/758] Revert "winevulkan: Remove extra index increment in virtual_queue_worker()." This reverts commit 27baf0f5af8793d108de8f8615b7a7a752db32a7. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index fe8af265b75..851085da048 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -4827,8 +4827,9 @@ static void *virtual_queue_worker(void *arg) /* Wait for all fences to have a pending signal */ for (i = 0; for_each_d3d12_semaphore(submit_unit, false, &sem, &timeline_value, i); i++) { - if ((wait = submit_unit->waits[i])) + if ((wait = submit_unit->waits[i++])) { + assert(wait); d3d12_semaphore_lock(sem); while (!wait->satisfied) From adcac79610588b87e30d8984a778a97f0ec09d07 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:30 -0600 Subject: [PATCH 694/758] Revert "winevulkan: Fix vkWaitSemaphores implementation with 32-bit time_t value." This reverts commit 99ec6833def7e0f8290c0062351c50431de36742. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 851085da048..82fb2051f9a 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -4394,7 +4393,6 @@ static VkResult wine_vk_wait_semaphores(VkDevice device_handle, const VkSemaphor unsigned int i, remaining_waits; VkSemaphore* semaphores_dup; uint64_t *values_dup; - int64_t tv_sec_wide; uint64_t phys_val; int wait_stat; VkResult res; @@ -4405,18 +4403,13 @@ static VkResult wine_vk_wait_semaphores(VkDevice device_handle, const VkSemaphor { clock_gettime(CLOCK_REALTIME, &start_time); - abs_timeout.tv_sec = tv_sec_wide = start_time.tv_sec + (timeout / NANOSECONDS_IN_A_SECOND); + abs_timeout.tv_sec = start_time.tv_sec + (timeout / NANOSECONDS_IN_A_SECOND); abs_timeout.tv_nsec = start_time.tv_nsec + (timeout % NANOSECONDS_IN_A_SECOND); if (abs_timeout.tv_nsec >= NANOSECONDS_IN_A_SECOND) { abs_timeout.tv_sec++; - tv_sec_wide++; abs_timeout.tv_nsec-=NANOSECONDS_IN_A_SECOND; } - - /* tv_sec is still! 32-bit on x86 */ - if (tv_sec_wide > abs_timeout.tv_sec) - abs_timeout.tv_sec = INT_MAX; } wait_info_dup.pSemaphores = semaphores_dup = calloc(wait_info->semaphoreCount, sizeof(VkSemaphore)); From 72755160ad7399da8dfe7d55572fbab5fc55eff8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:31 -0600 Subject: [PATCH 695/758] Revert "winevulkan: Flush virtual queue before providing native handle." This reverts commit 5152033a65b8f7e98d825973a849153c68c0d4cc. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 82fb2051f9a..148cb5918a3 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3639,15 +3639,6 @@ VkQueue WINAPI __wine_get_native_VkQueue(VkQueue handle) { struct wine_queue *queue = wine_queue_from_handle(handle); - if (is_virtual_queue(queue)) - { - FIXME("VR is using native handle of virtualized queue, this is untested.\n"); - pthread_mutex_lock(&queue->submissions_mutex); - while (queue->processing) - pthread_cond_wait(&queue->submissions_cond, &queue->submissions_mutex); - pthread_mutex_unlock(&queue->submissions_mutex); - } - return queue->queue; } From 6f0d6aee8df91da020854fd6acb9c31a0bdc7be5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:32 -0600 Subject: [PATCH 696/758] Revert "winevulkan: Add support for signalling VkFence from virtualized VkQueues." This reverts commit 221912a31286735ba9d281fb66bf919d95e68d4d. CW-Bug-Id: #22526 --- dlls/winevulkan/make_vulkan | 6 - dlls/winevulkan/vulkan.c | 285 +++---------------------------- dlls/winevulkan/vulkan_private.h | 20 --- 3 files changed, 19 insertions(+), 292 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index f680c130955..6d0d61937ec 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -202,10 +202,8 @@ FUNCTION_OVERRIDES = { # Device functions "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"}, - "vkCreateFence" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, - "vkDestroyFence" : {"dispatch" : True, "driver" : False, "thunk": ThunkType.NONE}, "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE}, "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE}, "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, @@ -219,9 +217,7 @@ FUNCTION_OVERRIDES = { "vkCreateBuffer" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkCreateImage" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE}, "vkGetSemaphoreCounterValue" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, - "vkResetFences" : {"dispatch" : True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkSignalSemaphore" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, - "vkWaitForFences" : {"dispatch": True, "driver": False, "thunk": ThunkType.PRIVATE}, "vkWaitSemaphores" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueBindSparse" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueSubmit" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, @@ -1196,8 +1192,6 @@ class VkHandle(object): return "wine_device_memory_from_handle({0})->memory".format(name) if self.name == "VkSemaphore": return "wine_semaphore_host_handle( wine_semaphore_from_handle({0}) )".format(name) - if self.name == "VkFence": - return "wine_fence_from_handle({0})->fence".format(name) if self.name == "VkPhysicalDevice": return "wine_phys_dev_from_handle({0})->phys_dev".format(name) if self.name == "VkQueue": diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 148cb5918a3..6ace73ea48e 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include "ntstatus.h" #define WIN32_NO_STATUS @@ -4526,9 +4524,8 @@ VkResult wine_vkWaitSemaphoresKHR(VkDevice device, const VkSemaphoreWaitInfo *wa return wine_vk_wait_semaphores(device, wait_info, timeout, true); } -VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo *submits_orig, VkFence fence_handle) +VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo *submits_orig, VkFence fence) { - struct wine_fence *fence = fence_handle ? wine_fence_from_handle(fence_handle) : NULL; struct conversion_context ctx; VkSubmitInfo *submits; unsigned int i, j; @@ -4550,12 +4547,7 @@ VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, submits[i].pCommandBuffers = out; } } - - - if (fence) - fence->queue = queue; - - ret = queue->device->funcs.p_vkQueueSubmit(queue->queue, submit_count, submits, fence ? fence->fence : VK_NULL_HANDLE); + ret = queue->device->funcs.p_vkQueueSubmit(queue->queue, submit_count, submits, fence); free_conversion_context(&ctx); return ret; } @@ -4565,7 +4557,6 @@ struct signal_op enum { SIGNAL_TYPE_SEMAPHORE, - SIGNAL_TYPE_FENCE, } signal_type; union @@ -4577,8 +4568,6 @@ struct signal_op bool khr; } semaphore; - - struct wine_fence *fence; }; struct list entry; @@ -4591,7 +4580,6 @@ static void *queue_signaller_worker(void *arg) struct signal_op *signal_op; VkSemaphore sem_handle; bool device_lost; - uint64_t buf; VkResult vr; for (;;) @@ -4635,19 +4623,6 @@ static void *queue_signaller_worker(void *arg) d3d12_semaphore_update_phys_val_locked(signal_op->semaphore.obj, signal_op->semaphore.phys_val); d3d12_semaphore_unlock(signal_op->semaphore.obj); } - else - { - if (!device_lost && (vr = queue->device->funcs.p_vkWaitForFences - (queue->device->device, 1, &signal_op->fence->fence, VK_TRUE, -1)) < 0) - { - /* likely GPU hang */ - fprintf(stderr, "winevulkan/queue_signaller_worker: Fence wait failed, vr %d.\n", vr); - continue; - } - - buf = 1; - assert( write(signal_op->fence->eventfd, &buf, sizeof(buf)) != -1 ); - } free(signal_op); } @@ -4780,7 +4755,6 @@ static void *virtual_queue_worker(void *arg) struct signal_op *signal_op; struct wine_semaphore *sem; struct pending_wait *wait; - struct wine_fence *fence; bool device_lost = false; uint64_t *timeline_value; unsigned int i; @@ -4833,10 +4807,10 @@ static void *virtual_queue_worker(void *arg) } if (submit_unit->submits) - vr = vk_queue_submit_unwrap(queue, submit_unit->submit_count, submit_unit->submits, submit_unit->fence); + vr = vk_queue_submit_unwrap(queue, submit_unit->submit_count, submit_unit->submits, VK_NULL_HANDLE); else vr = vk_queue_submit_2_unwrap(queue, submit_unit->submit_count, submit_unit->submits2, - submit_unit->fence, submit_unit->khr); + VK_NULL_HANDLE, submit_unit->khr); pthread_mutex_lock(&queue->signaller_mutex); @@ -4863,15 +4837,6 @@ static void *virtual_queue_worker(void *arg) d3d12_semaphore_unlock(sem); } - if (vr == VK_SUCCESS && (fence = wine_fence_from_handle(submit_unit->fence))) - { - signal_op = malloc(sizeof(*signal_op)); - signal_op->signal_type = SIGNAL_TYPE_FENCE; - signal_op->fence = fence; - - list_add_tail(&queue->signal_ops, &signal_op->entry); - } - pthread_cond_signal(&queue->signaller_cond); pthread_mutex_unlock(&queue->signaller_mutex); @@ -4881,12 +4846,6 @@ static void *virtual_queue_worker(void *arg) pthread_mutex_lock(&queue->submissions_mutex); queue->device_lost = device_lost = true; pthread_mutex_unlock(&queue->submissions_mutex); - - if ((fence = wine_fence_from_handle(submit_unit->fence))) - { - uint64_t buf = 1; - assert( write(fence->eventfd, &buf, sizeof(buf)) != -1 ); - } } free_submit_unit: @@ -4941,6 +4900,12 @@ static NTSTATUS virtual_queue_submit(struct wine_queue *queue, uint32_t submit_c uint64_t wait_value; bool device_lost; + if (fence != VK_NULL_HANDLE) + { + FIXME("Signalling fences in queue submissions involving D3D12-Fence compatible timeline semaphores not supported.\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + init_virtual_queue(queue); pthread_mutex_lock(&queue->submissions_mutex); @@ -4954,7 +4919,6 @@ static NTSTATUS virtual_queue_submit(struct wine_queue *queue, uint32_t submit_c submit_unit->submit_count = submit_count; submit_unit->submits = copy_VkSubmitInfo_array(&submit_unit->ctx, submits, submit_count); submit_unit->submits2 = NULL; - submit_unit->fence = fence; submit_unit->waits = NULL; submit_unit->khr = queue->device->phys_dev->api_version < VK_API_VERSION_1_2 || queue->device->phys_dev->instance->api_version < VK_API_VERSION_1_2; @@ -5010,11 +4974,6 @@ static NTSTATUS virtual_queue_submit(struct wine_queue *queue, uint32_t submit_c pthread_mutex_lock(&queue->submissions_mutex); queue->processing = true; - if (fence) - { - wine_fence_from_handle(fence)->queue = queue; - wine_fence_from_handle(fence)->wait_assist = true; - } list_add_tail(&queue->submissions, &submit_unit->entry); pthread_cond_signal(&queue->submissions_cond); pthread_mutex_unlock(&queue->submissions_mutex); @@ -5136,9 +5095,8 @@ static NTSTATUS virtual_queue_submit2(struct wine_queue *queue, uint32_t submit_ } VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo2 *submits_orig, - VkFence fence_handle, bool khr) + VkFence fence, bool khr) { - struct wine_fence *fence = fence_handle ? wine_fence_from_handle(fence_handle) : NULL; struct conversion_context ctx; VkSubmitInfo2 *submits; unsigned int i, j; @@ -5171,27 +5129,23 @@ VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_coun = wine_cmd_buffer_from_handle(submits[i].pCommandBufferInfos[j].commandBuffer)->command_buffer; } } - if (fence) - fence->queue = queue; - if (khr) - ret = queue->device->funcs.p_vkQueueSubmit2KHR(queue->queue, submit_count, submits, fence ? fence->fence : VK_NULL_HANDLE); + ret = queue->device->funcs.p_vkQueueSubmit2KHR(queue->queue, submit_count, submits, fence); else - ret = queue->device->funcs.p_vkQueueSubmit2(queue->queue, submit_count, submits, fence ? fence->fence : VK_NULL_HANDLE); - + ret = queue->device->funcs.p_vkQueueSubmit2(queue->queue, submit_count, submits, fence); free_conversion_context(&ctx); return ret; } -static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence_handle, bool khr) +static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence, bool khr) { struct wine_queue *queue = wine_queue_from_handle(queue_handle); unsigned int i, k; - TRACE("(%p, %u, %p, %s)\n", queue_handle, submit_count, submits, wine_dbgstr_longlong(fence_handle)); + TRACE("(%p, %u, %p, %s)\n", queue_handle, submit_count, submits, wine_dbgstr_longlong(fence)); if (is_virtual_queue(queue)) - return virtual_queue_submit2(queue, submit_count, submits, fence_handle, khr); + return virtual_queue_submit2(queue, submit_count, submits, fence, khr); for (i = 0; i < submit_count; i++) { @@ -5199,18 +5153,18 @@ static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, c { if (wine_semaphore_from_handle(submits[i].pWaitSemaphoreInfos[k].semaphore)->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - return virtual_queue_submit2(queue, submit_count, submits, fence_handle, khr); + return virtual_queue_submit2(queue, submit_count, submits, fence, khr); } for (k = 0; k < submits[i].signalSemaphoreInfoCount; k++) { if (wine_semaphore_from_handle(submits[i].pSignalSemaphoreInfos[k].semaphore)->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - return virtual_queue_submit2(queue, submit_count, submits, fence_handle, khr); + return virtual_queue_submit2(queue, submit_count, submits, fence, khr); } } - return vk_queue_submit_2_unwrap(queue, submit_count, submits, fence_handle, khr); + return vk_queue_submit_2_unwrap(queue, submit_count, submits, fence, khr); } VkResult wine_vkQueueSubmit2(VkQueue queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence) @@ -5349,207 +5303,6 @@ VkResult wine_vkQueueBindSparse(VkQueue queue_handle, uint32_t bind_info_count, return ret; } -VkResult wine_vkCreateFence(VkDevice device_handle, const VkFenceCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkFence *fence) -{ - struct wine_device *device = wine_device_from_handle(device_handle); - struct wine_fence *object; - VkResult vr; - - TRACE("(%p, %p, %p, %p)\n", device, create_info, allocator, fence); - - if (allocator) - FIXME("Support for allocation callbacks not implemented yet\n"); - - if (!(object = calloc(1, sizeof(*object)))) - return VK_ERROR_OUT_OF_HOST_MEMORY; - - if ((object->eventfd = eventfd(0, EFD_CLOEXEC)) == -1) - ERR("Failed to create eventfd for fence.\n"); - - if ((vr = device->funcs.p_vkCreateFence(device->device, create_info, NULL, &object->fence)) == VK_SUCCESS) - *fence = wine_fence_to_handle(object); - else - free(object); - - return vr; -} - -void wine_vkDestroyFence(VkDevice device_handle, VkFence fence_handle, const VkAllocationCallbacks *allocator) -{ - struct wine_device *device = wine_device_from_handle(device_handle); - struct wine_fence *fence = wine_fence_from_handle(fence_handle); - - TRACE("(%p, %p, %p)\n", device, fence, allocator); - - if (allocator) - FIXME("Support for allocation callbacks not implemented yet\n"); - - if (!fence_handle) - return; - - if (fence->eventfd != -1) - close(fence->eventfd); - - device->funcs.p_vkDestroyFence(device->device, fence->fence, allocator); - free(fence); -} - -static VkFence *unwrap_fence_array(const VkFence *in, uint32_t count, struct conversion_context *ctx) -{ - VkFence *out; - unsigned int i; - - if (!in || !count) return NULL; - - out = conversion_context_alloc(ctx, count * sizeof(*out)); - for (i = 0; i < count; ++i) - out[i] = in[i] ? wine_fence_from_handle(in[i])->fence : VK_NULL_HANDLE; - - return out; -} - -VkResult wine_vkResetFences(VkDevice device_handle, uint32_t fence_count, const VkFence *fences) -{ - struct wine_device *device = wine_device_from_handle(device_handle); - struct conversion_context ctx; - struct wine_fence *fence; - VkFence *fences_unwrap; - unsigned int i; - uint64_t buf; - VkResult vr; - - TRACE("(%p, %u, %p)\n", device, fence_count, fences); - - init_conversion_context(&ctx); - fences_unwrap = unwrap_fence_array(fences, fence_count, &ctx); - vr = device->funcs.p_vkResetFences(device->device, fence_count, fences_unwrap); - free_conversion_context(&ctx); - if (vr) - return vr; - - for (i = 0; i < fence_count; i++) - { - fence = wine_fence_from_handle(fences[i]); - - fence->queue = NULL; - fence->swapchain = NULL; - if (fence->wait_assist) - { - fence->wait_assist = false; - if (read(fence->eventfd, &buf, sizeof(buf)) == -1) - ERR("Failed to reset event fd.\n"); - } - } - - return VK_SUCCESS; -} - -VkResult wine_vkWaitForFences(VkDevice device_handle, uint32_t fence_count, const VkFence *fences, - VkBool32 wait_all, uint64_t timeout) -{ - struct wine_device *device = wine_device_from_handle(device_handle); - struct signal_op *signal_op; - bool assisted_wait = false; - struct wine_fence *fence; - struct pollfd *wait_fds; - struct pollfd wait_fd; - unsigned int i; - VkResult vr; - int ret; - - TRACE("(%p, %u, %p, %u, 0x%s)\n", device, fence_count, fences, wait_all, wine_dbgstr_longlong(timeout)); - - for (i = 0; i < fence_count; i++) - { - fence = wine_fence_from_handle(fences[i]); - if (!fence->wait_assist) - continue; - - if (!wait_all && fence_count > 1) - { - assisted_wait = true; - break; - } - - wait_fd.fd = fence->eventfd; - wait_fd.events = POLLIN; - ret = poll(&wait_fd, 1, timeout / 1000000); - if (ret == -1) - { - ERR("Failed to poll wait assisted fence.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - if (!ret) - return VK_TIMEOUT; - - if (wait_fd.revents & (POLLERR | POLLHUP | POLLNVAL)) - ERR("Polling on fd %d returned %#x.", fence->eventfd, wait_fd.revents); - return VK_SUCCESS; - } - - if (assisted_wait) - { - /* Turn all non assisted waits into assisted waits, then poll on all */ - wait_fds = malloc( sizeof(wait_fds[0]) * fence_count ); - - for (i = 0; i < fence_count; i++) - { - if (!fence->wait_assist) - { - assert(fence->queue || fence->swapchain); - - if (fence->queue) - { - fence->wait_assist = true; - - /* If virtual-queue requiring work was submitted after the work signalling this mutex, - * we will end up unnecessarily waiting on that work first, - * but this will only happen once per queue */ - init_virtual_queue(fence->queue); - - signal_op = malloc(sizeof(*signal_op)); - signal_op->signal_type = SIGNAL_TYPE_FENCE; - signal_op->fence = fence; - - pthread_mutex_lock(&fence->queue->signaller_mutex); - list_add_tail(&fence->queue->signal_ops, &signal_op->entry); - pthread_cond_signal(&fence->queue->signaller_cond); - pthread_mutex_unlock(&fence->queue->signaller_mutex); - } - else - { - FIXME("Wait assist for swapchain signaled fences not supported.\n"); - free(wait_fds); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - - wait_fds[i].fd = fence->eventfd; - wait_fds[i].events = POLLIN; - } - - if (poll(wait_fds, fence_count, timeout / 1000000) == -1) - { - ERR("Failed to poll wait assisted fences.\n"); - vr = VK_ERROR_OUT_OF_HOST_MEMORY; - } - - free(wait_fds); - } - else - { - struct conversion_context ctx; - VkFence *fences_unwrap; - - init_conversion_context(&ctx); - fences_unwrap = unwrap_fence_array(fences, fence_count, &ctx); - vr = device->funcs.p_vkWaitForFences(device->device, fence_count, fences_unwrap, wait_all, timeout); - free_conversion_context(&ctx); - } - - return vr; -} - VkResult wine_vkQueueWaitIdle(VkQueue queue_handle) { struct wine_queue *queue = wine_queue_from_handle(queue_handle); diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 864fc392c2c..218c2be9efe 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -393,26 +393,6 @@ static inline VkSemaphore wine_semaphore_host_handle(struct wine_semaphore *sema return semaphore->semaphore; } -struct wine_fence -{ - VkFence fence; - - struct wine_queue *queue; - struct wine_swapchain *swapchain; - bool wait_assist; - int eventfd; -}; - -static inline struct wine_fence *wine_fence_from_handle(VkFence handle) -{ - return (struct wine_fence *)(uintptr_t)handle; -} - -static inline VkFence wine_fence_to_handle(struct wine_fence *fence) -{ - return (VkFence)(uintptr_t)fence; -} - static inline void *conversion_context_alloc(struct conversion_context *pool, size_t size) { if (pool->used + size <= sizeof(pool->buffer)) From cd62b8e35209b71dfdec6ca4f8f38b33b2b3ace8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:33 -0600 Subject: [PATCH 697/758] Revert "winevulkan: Support waiting for and signalling D3D12-Fence style timeline semaphores in Vulkan Queues." This reverts commit ac20bc2ef828f9347d0308ac7ae19cf8e884c59a. CW-Bug-Id: #22526 --- dlls/winevulkan/make_vulkan | 52 +-- dlls/winevulkan/vulkan.c | 691 ++----------------------------- dlls/winevulkan/vulkan_private.h | 18 - 3 files changed, 50 insertions(+), 711 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 6d0d61937ec..b7d57f2964e 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -220,9 +220,8 @@ FUNCTION_OVERRIDES = { "vkSignalSemaphore" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkWaitSemaphores" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueBindSparse" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, - "vkQueueSubmit" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, + "vkQueueSubmit" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueSubmit2" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, - "vkQueueWaitIdle" : {"dispatch": True, "driver": False, "thunk" : ThunkType.NONE}, # VK_KHR_surface "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE}, @@ -312,16 +311,11 @@ STRUCT_CHAIN_CONVERSIONS = { "VkPhysicalDeviceImageFormatInfo2": [], "VkPhysicalDeviceExternalSemaphoreInfo": [], "VkSemaphoreCreateInfo": ["VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR"], - "VkSubmitInfo": ["VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR"], + "VkSubmitInfo": [], "VkSubmitInfo2": [], "VkBindSparseInfo" : [], } -STRUCT_COPY = { - "VkSubmitInfo", - "VkSubmitInfo2", -}; - # Some struct members are conditionally ignored and callers are free to leave them uninitialized. # We can't deduce that from XML, so we allow expressing it here. MEMBER_LENGTH_EXPRESSIONS = { @@ -1409,9 +1403,6 @@ class VkVariable(object): if struct.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const): conversions.append(StructConversionFunction(struct, Direction.OUTPUT, conv, unwrap, is_const)) - if struct.name in STRUCT_COPY: - conversions.append(StructConversionFunction(struct, Direction.INPUT, False, unwrap, is_const, True)) - if self.is_static_array() or self.is_dynamic_array(): for conv in [False, True]: if self.needs_conversion(conv, unwrap, Direction.INPUT, parent_const): @@ -1541,7 +1532,7 @@ class VkMember(VkVariable): values=values, object_type=object_type, bit_width=bit_width, returnedonly=returnedonly, parent=parent, selection=selection, selector=selector) - def copy(self, input, output, direction, conv, unwrap, copy): + def copy(self, input, output, direction, conv, unwrap): """ Helper method for use by conversion logic to generate a C-code statement to copy this member. - `conv` indicates whether the statement is in a struct alignment conversion path. """ @@ -1599,8 +1590,6 @@ class VkMember(VkVariable): elif self.is_static_array(): bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type) return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count) - elif self.is_dynamic_array() and copy: - return "MEMDUP(ctx, {0}{1}, {2}{1}, {3});\n".format(output, self.name, input, self.get_dyn_array_len(input, conv)) elif direction == Direction.INPUT: return "{0}{1} = {2};\n".format(output, self.name, self.value(input, conv)) elif conv and direction == Direction.OUTPUT and self.is_pointer(): @@ -2323,25 +2312,21 @@ class VkStruct(Sequence): class StructConversionFunction(object): - def __init__(self, struct, direction, conv, unwrap, const, copy=False): + def __init__(self, struct, direction, conv, unwrap, const): self.direction = direction self.operand = struct self.type = struct.name self.conv = conv self.unwrap = unwrap or not self.operand.needs_unwrapping() self.const = const - self.copy = copy - if copy: - name = "copy_{0}".format(self.type) - else: - name = "convert_{0}_".format(self.type) - win_type = "win32" if self.conv else "win64" - host_part = "host" if self.unwrap else "unwrapped_host" - if self.direction == Direction.INPUT: - name += "{0}_to_{1}".format(win_type, host_part) - else: # Direction.OUTPUT - name += "{0}_to_{1}".format(host_part, win_type) + name = "convert_{0}_".format(self.type) + win_type = "win32" if self.conv else "win64" + host_part = "host" if self.unwrap else "unwrapped_host" + if self.direction == Direction.INPUT: + name += "{0}_to_{1}".format(win_type, host_part) + else: # Direction.OUTPUT + name += "{0}_to_{1}".format(host_part, win_type) self.name = name def __eq__(self, other): @@ -2373,7 +2358,7 @@ class StructConversionFunction(object): body = "" - if not self.conv and not self.copy: + if not self.conv: body += "#ifdef _WIN64\n" needs_alloc = self.direction != Direction.OUTPUT and self.operand.needs_alloc(self.conv, self.unwrap) @@ -2383,11 +2368,8 @@ class StructConversionFunction(object): if self.direction == Direction.OUTPUT and self.const: win_type = "const " + win_type - if self.copy: - body += "void {0}(".format(self.name) - else: - body += "static inline void {0}(".format(self.name) if self.conv: + body += "static inline void {0}(".format(self.name) if self.direction == Direction.OUTPUT: params = ["const {0} *in".format(self.type), "{0} *out".format(win_type)] @@ -2404,6 +2386,8 @@ class StructConversionFunction(object): body += ")\n" else: + body += "static inline void {0}(".format(self.name) + params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)] # Generate parameter list @@ -2443,7 +2427,7 @@ class StructConversionFunction(object): body += " || ".join("selector == {}".format(s) for s in m.selection) body += ")\n " - body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap, self.copy) + body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap) if needs_extensions: if self.conv and self.direction == Direction.INPUT: @@ -2503,7 +2487,7 @@ class StructConversionFunction(object): copy_body += ident + "out_ext->pNext = NULL;\n" continue - copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True, self.copy) + copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True) # Generate the definition of "in_ext" if we need it if "in_ext->" in copy_body: @@ -2538,7 +2522,7 @@ class StructConversionFunction(object): body += " FIXME(\"Unexpected pNext\\n\");\n" body += "}\n" - if not self.conv and not self.copy: + if not self.conv: body += "#endif /* _WIN64 */\n" body += "\n" diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 6ace73ea48e..d0b90293344 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -36,7 +36,6 @@ #include "winnt.h" #include "winioctl.h" #include "wine/server.h" -#include "wine/list.h" #include "vulkan_private.h" #include "wine/vulkan_driver.h" @@ -243,7 +242,6 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance struct wine_phys_dev *object; uint32_t num_host_properties, num_properties = 0; VkExtensionProperties *host_properties = NULL; - VkPhysicalDeviceProperties physdev_properties; BOOL have_external_memory_host = FALSE; VkResult res; unsigned int i, j; @@ -255,9 +253,6 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance object->handle = handle; object->phys_dev = phys_dev; - instance->funcs.p_vkGetPhysicalDeviceProperties(phys_dev, &physdev_properties); - object->api_version = physdev_properties.apiVersion; - handle->base.unix_handle = (uintptr_t)object; WINE_VK_ADD_DISPATCHABLE_MAPPING(instance, handle, phys_dev, object); @@ -402,14 +397,6 @@ static void wine_vk_device_get_queues(struct wine_device *device, queue->queue_index = i; queue->flags = flags; - pthread_mutex_init(&queue->submissions_mutex, NULL); - pthread_cond_init(&queue->submissions_cond, NULL); - list_init(&queue->submissions); - - pthread_mutex_init(&queue->signaller_mutex, NULL); - pthread_cond_init(&queue->signaller_cond, NULL); - list_init(&queue->signal_ops); - /* The Vulkan spec says: * * "vkGetDeviceQueue must only be used to get queues that were created @@ -579,11 +566,6 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de return VK_SUCCESS; } -static bool is_virtual_queue(struct wine_queue *queue) -{ - return __atomic_load_n(&queue->virtual_queue, __ATOMIC_ACQUIRE); -} - /* Helper function used for freeing a device structure. This function supports full * and partial object cleanups and can thus be used for vkCreateDevice failures. */ @@ -600,28 +582,6 @@ static void wine_vk_device_free(struct wine_device *device) for (i = 0; i < device->queue_count; i++) { queue = &device->queues[i]; - - if (is_virtual_queue(queue)) - { - pthread_mutex_lock(&queue->submissions_mutex); - pthread_mutex_lock(&queue->signaller_mutex); - queue->stop = 1; - pthread_mutex_unlock(&queue->submissions_mutex); - pthread_mutex_unlock(&queue->signaller_mutex); - - pthread_cond_signal(&queue->submissions_cond); - pthread_cond_signal(&queue->signaller_cond); - - pthread_join(queue->virtual_queue_thread, NULL); - pthread_join(queue->signal_thread, NULL); - } - - pthread_mutex_destroy(&queue->submissions_mutex); - pthread_mutex_destroy(&queue->signaller_mutex); - - pthread_cond_destroy(&queue->submissions_cond); - pthread_cond_destroy(&queue->signaller_cond); - if (queue && queue->queue) WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, queue); } @@ -1097,8 +1057,6 @@ VkResult wine_vkCreateInstance(const VkInstanceCreateInfo *create_info, app_info->engineVersion); TRACE("API version %#x.\n", app_info->apiVersion); - object->api_version = app_info->apiVersion; - if (app_info->pEngineName && !strcmp(app_info->pEngineName, "idTech")) object->quirks |= WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR; } @@ -3741,7 +3699,7 @@ static void d3d12_semaphore_unlock(struct wine_semaphore *semaphore) /* returns -1 when there is no queued update that would satisfy the wait */ static uint64_t d3d12_semaphore_try_get_wait_value_locked(struct wine_semaphore *semaphore, uint64_t virtual_value, - struct wine_queue *waiting_queue) + struct VkQueue_T *waiting_queue) { struct pending_update *update; uint64_t ret = -1; @@ -3750,19 +3708,6 @@ static uint64_t d3d12_semaphore_try_get_wait_value_locked(struct wine_semaphore if (semaphore->d3d12_fence_shm->virtual_value >= virtual_value) return 0; - for (i = 0; i < semaphore->d3d12_fence_shm->pending_updates_count; i++) - { - update = &semaphore->d3d12_fence_shm->pending_updates[i]; - - if (update->virtual_value < virtual_value) - continue; - - if (update->signalling_pid == getpid() && waiting_queue && update->signalling_queue == waiting_queue) - return 0; - - ret = min(ret, update->physical_value); - } - return ret; } @@ -3819,34 +3764,6 @@ static void d3d12_semaphore_satisfy_waits_locked(struct wine_semaphore *semaphor } } -static uint64_t d3d12_semaphore_add_pending_signal_locked(struct wine_semaphore *semaphore, uint64_t virtual_value, - struct wine_queue *signalling_queue) -{ - struct pending_update *update; - - if (semaphore->d3d12_fence_shm->pending_updates_count == ARRAY_SIZE(semaphore->d3d12_fence_shm->pending_updates)) - { - /* Called from Unix thread, can't use Wine traces. */ - fprintf(stderr, "winevulkan/d3d12_semaphore_add_pending_signal_locked: maximum concurrent signals exceeded.\n"); - return 0; - } - - update = &semaphore->d3d12_fence_shm->pending_updates[ - semaphore->d3d12_fence_shm->pending_updates_count++]; - - update->virtual_value = virtual_value; - update->physical_value = ++semaphore->d3d12_fence_shm->counter; - update->signalling_pid = getpid(); - update->signalling_queue = signalling_queue; - - return update->physical_value; -} - -static struct pending_update d3d12_semaphore_peek_added_signal_locked(struct wine_semaphore *semaphore) -{ - return semaphore->d3d12_fence_shm->pending_updates[semaphore->d3d12_fence_shm->pending_updates_count - 1]; -} - static bool d3d12_semaphore_pop_pending_signal_locked(struct wine_semaphore *semaphore, uint64_t phys_val, struct pending_update *ret) { struct pending_update *update; @@ -4288,41 +4205,27 @@ static NTSTATUS wine_vk_signal_semaphore(VkDevice device, const VkSemaphoreSigna if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) { - d3d12_semaphore_lock(semaphore); + VkSemaphoreSignalInfo step_signal_info; - /* vkWaitSemaphore w/ WAIT_ANY wakes on every physical value increment to check if the wait is satisfied, so - if there are no scheduled signals, step the physical value */ - if ((vr = vk_get_semaphore_counter_value(device, signal_info->semaphore, &phys_val, khr)) != VK_SUCCESS) - { - d3d12_semaphore_unlock(semaphore); - return vr; - } - - d3d12_semaphore_update_phys_val_locked(semaphore, phys_val); - - if (!semaphore->d3d12_fence_shm->pending_updates_count) - { - VkSemaphoreSignalInfo step_signal_info; + d3d12_semaphore_lock(semaphore); - assert(semaphore->d3d12_fence_shm->counter == phys_val); - phys_val++; + assert(semaphore->d3d12_fence_shm->counter == phys_val); + phys_val++; - semaphore->d3d12_fence_shm->counter = semaphore->d3d12_fence_shm->physical_value = phys_val; + semaphore->d3d12_fence_shm->counter = semaphore->d3d12_fence_shm->physical_value = phys_val; - step_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO; - step_signal_info.pNext = NULL; - step_signal_info.semaphore = signal_info->semaphore; - step_signal_info.value = phys_val; + step_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO; + step_signal_info.pNext = NULL; + step_signal_info.semaphore = signal_info->semaphore; + step_signal_info.value = phys_val; - vr = vk_signal_semaphore(device, &step_signal_info, khr); - if (vr != VK_SUCCESS) - { - d3d12_semaphore_unlock(semaphore); - return vr; - } + vr = vk_signal_semaphore(device, &step_signal_info, khr); + if (vr != VK_SUCCESS) + { + d3d12_semaphore_unlock(semaphore); + return vr; } - /* If a queue is already waiting on the pending physical value of a previous submit, this won't wake it up. */ d3d12_semaphore_satisfy_waits_locked(semaphore, signal_info->value, 0); semaphore->d3d12_fence_shm->virtual_value = signal_info->value; @@ -4552,460 +4455,33 @@ VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, return ret; } -struct signal_op -{ - enum - { - SIGNAL_TYPE_SEMAPHORE, - } signal_type; - - union - { - struct - { - struct wine_semaphore *obj; - uint64_t phys_val; - - bool khr; - } semaphore; - }; - - struct list entry; -}; - -static void *queue_signaller_worker(void *arg) -{ - struct wine_queue *queue = arg; - VkSemaphoreWaitInfo wait_info; - struct signal_op *signal_op; - VkSemaphore sem_handle; - bool device_lost; - VkResult vr; - - for (;;) - { - pthread_mutex_lock(&queue->signaller_mutex); - - while (!queue->stop && list_empty(&queue->signal_ops)) - pthread_cond_wait(&queue->signaller_cond, &queue->signaller_mutex); - - if (queue->stop) - { - assert( list_empty(&queue->signal_ops) ); - pthread_mutex_unlock(&queue->signaller_mutex); - return NULL; - } - - signal_op = LIST_ENTRY(list_head(&queue->signal_ops), struct signal_op, entry); - list_remove(&signal_op->entry); - - device_lost = queue->device_lost; - - pthread_mutex_unlock(&queue->signaller_mutex); - - if (signal_op->signal_type == SIGNAL_TYPE_SEMAPHORE) - { - wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; - wait_info.pNext = NULL; - wait_info.flags = 0; - wait_info.semaphoreCount = 1; - sem_handle = wine_semaphore_to_handle(signal_op->semaphore.obj); - wait_info.pSemaphores = &sem_handle; - wait_info.pValues = &signal_op->semaphore.phys_val; - if (!device_lost && (vr = vk_wait_semaphores(queue->device, &wait_info, -1, signal_op->semaphore.khr)) < 0) - { - /* likely GPU hang */ - fprintf(stderr, "winevulkan/queue_signaller_worker: Semaphore wait failed, vr %d.\n", vr); - continue; - } - - d3d12_semaphore_lock(signal_op->semaphore.obj); - d3d12_semaphore_update_phys_val_locked(signal_op->semaphore.obj, signal_op->semaphore.phys_val); - d3d12_semaphore_unlock(signal_op->semaphore.obj); - } - - free(signal_op); - } - - return NULL; -} - -struct struct_chain_def -{ - VkStructureType sType; - unsigned int size; -}; - -void copy_VkSubmitInfo(struct conversion_context *ctx, const VkSubmitInfo *in, VkSubmitInfo *out); -static VkSubmitInfo *copy_VkSubmitInfo_array(struct conversion_context *ctx, const VkSubmitInfo *in, uint32_t submit_count) -{ - VkSubmitInfo *out = conversion_context_alloc(ctx, sizeof(*out) * submit_count); - unsigned int i; - - for (i = 0; i < submit_count; i++) - copy_VkSubmitInfo(ctx, &in[i], &out[i]); - return out; -} - -void copy_VkSubmitInfo2(struct conversion_context *ctx, const VkSubmitInfo2 *in, VkSubmitInfo2 *out); -static VkSubmitInfo2 *copy_VkSubmitInfo2_array(struct conversion_context *ctx, const VkSubmitInfo2 *in, uint32_t submit_count) -{ - VkSubmitInfo2 *out = conversion_context_alloc(ctx, sizeof(*out) * submit_count); - unsigned int i; - - for (i = 0; i < submit_count; i++) - copy_VkSubmitInfo2(ctx, &in[i], &out[i]); - return out; -} - -struct queue_submit_unit -{ - uint32_t submit_count; - VkSubmitInfo *submits; - VkSubmitInfo2 *submits2; - VkFence fence; - bool khr; - - struct pending_wait **waits; - - struct list entry; - struct conversion_context ctx; -}; - -/* Abstracts away the differences between VkSubmitInfo and VkSubmitInfo2. */ -static bool for_each_d3d12_semaphore(struct queue_submit_unit *unit, bool signal, - struct wine_semaphore **semaphore_out, uint64_t **value_out, uint32_t counter) -{ - VkTimelineSemaphoreSubmitInfo *timeline_values; - struct wine_semaphore *semaphore; - unsigned int i, j, k; - uint32_t sem_count; - - for (i = 0, k = 0; i < unit->submit_count; i++) - { - if (unit->submits) - { - timeline_values = wine_vk_find_struct(&unit->submits[i], TIMELINE_SEMAPHORE_SUBMIT_INFO); - - if (signal) - sem_count = unit->submits[i].signalSemaphoreCount; - else - sem_count = unit->submits[i].waitSemaphoreCount; - - for (j = 0; j < sem_count; j++) - { - if (signal) - semaphore = wine_semaphore_from_handle(unit->submits[i].pSignalSemaphores[j]); - else - semaphore = wine_semaphore_from_handle(unit->submits[i].pWaitSemaphores[j]); - - if (semaphore->handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - continue; - - if (k++ == counter) - { - *semaphore_out = semaphore; - if (signal) - *value_out = (uint64_t *) &timeline_values->pSignalSemaphoreValues[j]; - else - *value_out = (uint64_t *) &timeline_values->pWaitSemaphoreValues[j]; - return true; - } - } - } - else - { - if (signal) - sem_count = unit->submits2[i].signalSemaphoreInfoCount; - else - sem_count = unit->submits2[i].waitSemaphoreInfoCount; - - for (j = 0; j < sem_count; j++) - { - if (signal) - semaphore = wine_semaphore_from_handle(unit->submits2[i].pSignalSemaphoreInfos[j].semaphore); - else - semaphore = wine_semaphore_from_handle(unit->submits2[i].pWaitSemaphoreInfos[j].semaphore); - - if (semaphore->handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - continue; - - if (k++ == counter) - { - *semaphore_out = semaphore; - if (signal) - *value_out = (uint64_t *) &unit->submits2[i].pSignalSemaphoreInfos[j].value; - else - *value_out = (uint64_t *) &unit->submits2[i].pWaitSemaphoreInfos[j].value; - return true; - } - } - } - } - - return false; -} - -VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence, bool khr); - -static void *virtual_queue_worker(void *arg) -{ - struct wine_queue *queue = (struct wine_queue *)arg; - struct queue_submit_unit *submit_unit; - struct signal_op *signal_op; - struct wine_semaphore *sem; - struct pending_wait *wait; - bool device_lost = false; - uint64_t *timeline_value; - unsigned int i; - VkResult vr; - - for (;;) - { - pthread_mutex_lock(&queue->submissions_mutex); - - while (!queue->stop && list_empty(&queue->submissions)) - pthread_cond_wait(&queue->submissions_cond, &queue->submissions_mutex); - - if (queue->stop) - { - assert( list_empty(&queue->submissions) ); - pthread_mutex_unlock(&queue->submissions_mutex); - return NULL; - } - - submit_unit = LIST_ENTRY(list_head(&queue->submissions), struct queue_submit_unit, entry); - list_remove(&submit_unit->entry); - - pthread_mutex_unlock(&queue->submissions_mutex); - - if (device_lost) - goto free_submit_unit; - - /* Wait for all fences to have a pending signal */ - for (i = 0; for_each_d3d12_semaphore(submit_unit, false, &sem, &timeline_value, i); i++) - { - if ((wait = submit_unit->waits[i++])) - { - assert(wait); - d3d12_semaphore_lock(sem); - - while (!wait->satisfied) - pthread_cond_wait(&wait->cond, &sem->d3d12_fence_shm->mutex); - - *timeline_value = d3d12_semaphore_pop_wait_locked(sem, wait); - - d3d12_semaphore_unlock(sem); - } - } - - for (i = 0; for_each_d3d12_semaphore(submit_unit, true, &sem, &timeline_value, i); i++) - { - d3d12_semaphore_lock(sem); - - *timeline_value = d3d12_semaphore_add_pending_signal_locked(sem, *timeline_value, queue); - } - - if (submit_unit->submits) - vr = vk_queue_submit_unwrap(queue, submit_unit->submit_count, submit_unit->submits, VK_NULL_HANDLE); - else - vr = vk_queue_submit_2_unwrap(queue, submit_unit->submit_count, submit_unit->submits2, - VK_NULL_HANDLE, submit_unit->khr); - - pthread_mutex_lock(&queue->signaller_mutex); - - for (i = 0; for_each_d3d12_semaphore(submit_unit, true, &sem, &timeline_value, i); i++) - { - if (vr == VK_SUCCESS) - { - struct pending_update added_signal = d3d12_semaphore_peek_added_signal_locked(sem); - d3d12_semaphore_satisfy_waits_locked(sem, added_signal.virtual_value, added_signal.physical_value); - - signal_op = malloc(sizeof(*signal_op)); - signal_op->signal_type = SIGNAL_TYPE_SEMAPHORE; - signal_op->semaphore.obj = sem; - signal_op->semaphore.phys_val = added_signal.physical_value; - signal_op->semaphore.khr = submit_unit->khr; - - list_add_tail(&queue->signal_ops, &signal_op->entry); - } - else - { - d3d12_semaphore_pop_pending_signal_locked(sem, *timeline_value, NULL); - } - - d3d12_semaphore_unlock(sem); - } - - pthread_cond_signal(&queue->signaller_cond); - pthread_mutex_unlock(&queue->signaller_mutex); - - if (vr != VK_SUCCESS) - { - fprintf(stderr, "winevulkan/virtual_queue_worker: queue submission failed with %d, treating as DEVICE_LOST.\n", vr); - pthread_mutex_lock(&queue->submissions_mutex); - queue->device_lost = device_lost = true; - pthread_mutex_unlock(&queue->submissions_mutex); - } - -free_submit_unit: - free_conversion_context(&submit_unit->ctx); - free(submit_unit->waits); - free(submit_unit); - - pthread_mutex_lock(&queue->submissions_mutex); - if (list_empty(&queue->submissions)) - { - queue->processing = false; - } - pthread_cond_signal(&queue->submissions_cond); - pthread_mutex_unlock(&queue->submissions_mutex); - } - - return NULL; -} - -static void init_virtual_queue(struct wine_queue *queue) -{ - if (is_virtual_queue(queue)) - return; - - pthread_mutex_lock(&queue->submissions_mutex); - - if (queue->virtual_queue) - { - pthread_mutex_unlock(&queue->submissions_mutex); - return; - } - - __atomic_store_n(&queue->virtual_queue, 1, __ATOMIC_RELEASE); - - pthread_create(&queue->virtual_queue_thread, NULL, virtual_queue_worker, queue); - pthread_create(&queue->signal_thread, NULL, queue_signaller_worker, queue); - - queue->virtual_queue = true; - - pthread_mutex_unlock(&queue->submissions_mutex); -} - -static NTSTATUS virtual_queue_submit(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo *submits, - VkFence fence, void *submits_win_ptr) -{ - VkTimelineSemaphoreSubmitInfo *timeline_submit_info, *host_timeline_values; - const VkSubmitInfo *submits_win = submits_win_ptr; - VkD3D12FenceSubmitInfoKHR *d3d12_submit_info; - struct queue_submit_unit *submit_unit; - struct wine_semaphore *sem; - unsigned int i, j, k; - uint64_t wait_value; - bool device_lost; - - if (fence != VK_NULL_HANDLE) - { - FIXME("Signalling fences in queue submissions involving D3D12-Fence compatible timeline semaphores not supported.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - init_virtual_queue(queue); - - pthread_mutex_lock(&queue->submissions_mutex); - device_lost = queue->device_lost; - pthread_mutex_unlock(&queue->submissions_mutex); - if (device_lost) - return VK_ERROR_DEVICE_LOST; - - submit_unit = malloc(sizeof(*submit_unit)); - init_conversion_context(&submit_unit->ctx); - submit_unit->submit_count = submit_count; - submit_unit->submits = copy_VkSubmitInfo_array(&submit_unit->ctx, submits, submit_count); - submit_unit->submits2 = NULL; - submit_unit->waits = NULL; - submit_unit->khr = queue->device->phys_dev->api_version < VK_API_VERSION_1_2 || - queue->device->phys_dev->instance->api_version < VK_API_VERSION_1_2; - - /* As D3D12 fences are rewindable, we add the wait synchronously as not to miss a temporarily signalled value - between vkQueueSubmit and processing the submit unit*/ - for (i = 0, k = 0; i < submit_count; i++) - { - timeline_submit_info = wine_vk_find_struct(&submits[i], TIMELINE_SEMAPHORE_SUBMIT_INFO); - d3d12_submit_info = wine_vk_find_struct(&submits_win[i], D3D12_FENCE_SUBMIT_INFO_KHR); - - host_timeline_values = wine_vk_find_struct(&submit_unit->submits[i], TIMELINE_SEMAPHORE_SUBMIT_INFO); - - if (d3d12_submit_info && !host_timeline_values) - { - host_timeline_values = conversion_context_alloc(&submit_unit->ctx, sizeof(*host_timeline_values)); - host_timeline_values->sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; - host_timeline_values->pNext = submit_unit->submits[i].pNext; - host_timeline_values->waitSemaphoreValueCount = d3d12_submit_info->waitSemaphoreValuesCount; - MEMDUP(&submit_unit->ctx, host_timeline_values->pWaitSemaphoreValues, d3d12_submit_info->pWaitSemaphoreValues, - d3d12_submit_info->waitSemaphoreValuesCount); - host_timeline_values->signalSemaphoreValueCount = d3d12_submit_info->signalSemaphoreValuesCount; - MEMDUP(&submit_unit->ctx, host_timeline_values->pSignalSemaphoreValues, d3d12_submit_info->pSignalSemaphoreValues, - d3d12_submit_info->signalSemaphoreValuesCount); - submit_unit->submits[i].pNext = host_timeline_values; - } - - for (j = 0; j < submits[i].waitSemaphoreCount; j++) - { - sem = wine_semaphore_from_handle(submits[i].pWaitSemaphores[j]); - - if (sem->handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - continue; - - if (timeline_submit_info) - wait_value = timeline_submit_info->pWaitSemaphoreValues[j]; - else - wait_value = d3d12_submit_info->pWaitSemaphoreValues[j]; - - submit_unit->waits = realloc(submit_unit->waits, (k + 1) * sizeof(*submit_unit->waits)); - submit_unit->waits[k] = NULL; - - d3d12_semaphore_lock(sem); - - if ((((uint64_t*)host_timeline_values->pWaitSemaphoreValues)[j] = - d3d12_semaphore_try_get_wait_value_locked(sem, wait_value, queue)) == -1) - submit_unit->waits[k] = d3d12_semaphore_push_wait_locked(sem, wait_value); - - d3d12_semaphore_unlock(sem); - k++; - } - } - - pthread_mutex_lock(&queue->submissions_mutex); - queue->processing = true; - list_add_tail(&queue->submissions, &submit_unit->entry); - pthread_cond_signal(&queue->submissions_cond); - pthread_mutex_unlock(&queue->submissions_mutex); - - return VK_SUCCESS; -} - -VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits, VkFence fence, - void *submits_win) +VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits, VkFence fence) { struct wine_queue *queue = wine_queue_from_handle(queue_handle); unsigned int i, k; TRACE("(%p %u %p 0x%s)\n", queue_handle, submit_count, submits, wine_dbgstr_longlong(fence)); - if (is_virtual_queue(queue)) - return virtual_queue_submit(queue, submit_count, submits, fence, submits_win); - for (i = 0; i < submit_count; i++) { for (k = 0; k < submits[i].waitSemaphoreCount; k++) { if (wine_semaphore_from_handle(submits[i].pWaitSemaphores[k])->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - return virtual_queue_submit(queue, submit_count, submits, fence, submits_win); + { + FIXME("Queue submissions with waits on D3D12-Fence compatible timeline semaphores not supported.\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } } for (k = 0; k < submits[i].signalSemaphoreCount; k++) { if (wine_semaphore_from_handle(submits[i].pSignalSemaphores[k])->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - return virtual_queue_submit(queue, submit_count, submits, fence, submits_win); + { + FIXME("Queue submissions with signalling D3D12-Fence compatible timeline semaphores not supported.\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } } } return vk_queue_submit_unwrap(queue, submit_count, submits, fence); @@ -5023,77 +4499,6 @@ static void duplicate_array_for_unwrapping(struct conversion_context *ctx, void *ptr = out; } -static NTSTATUS virtual_queue_submit2(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence, bool khr) -{ - VkSemaphoreSubmitInfo *sem_submit_info; - struct queue_submit_unit *submit_unit; - VkSubmitInfo2 *queue_submit; - struct wine_semaphore *sem; - unsigned int i, j, k; - uint64_t wait_value; - bool device_lost; - - init_virtual_queue(queue); - - pthread_mutex_lock(&queue->submissions_mutex); - device_lost = queue->device_lost; - pthread_mutex_unlock(&queue->submissions_mutex); - if (device_lost) - return VK_ERROR_DEVICE_LOST; - - submit_unit = malloc(sizeof(*submit_unit)); - init_conversion_context(&submit_unit->ctx); - submit_unit->submit_count = submit_count; - submit_unit->submits = NULL; - submit_unit->submits2 = copy_VkSubmitInfo2_array(&submit_unit->ctx, submits, submit_count); - submit_unit->fence = fence; - submit_unit->waits = NULL; - submit_unit->khr = khr; - - /* As D3D12 fences are rewindable, we add the wait synchronously as not to miss a temporarily signalled value - between vkQueueSubmit and processing the submit unit */ - for (i = 0, k = 0; i < submit_count; i++) - { - queue_submit = &submit_unit->submits2[i]; - - for (j = 0; j < queue_submit->waitSemaphoreInfoCount; j++) - { - sem_submit_info = (VkSemaphoreSubmitInfo *) &queue_submit->pWaitSemaphoreInfos[j]; - sem = wine_semaphore_from_handle(sem_submit_info->semaphore); - - if (sem->handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - continue; - - wait_value = sem_submit_info->value; - - submit_unit->waits = realloc(submit_unit->waits, (k + 1) * sizeof(*submit_unit->waits)); - submit_unit->waits[k] = NULL; - - d3d12_semaphore_lock(sem); - - if ((sem_submit_info->value = - d3d12_semaphore_try_get_wait_value_locked(sem, wait_value, queue)) == -1) - submit_unit->waits[k] = d3d12_semaphore_push_wait_locked(sem, wait_value); - - d3d12_semaphore_unlock(sem); - k++; - } - } - - pthread_mutex_lock(&queue->submissions_mutex); - queue->processing = true; - if (fence) - { - wine_fence_from_handle(fence)->queue = queue; - wine_fence_from_handle(fence)->wait_assist = true; - } - list_add_tail(&queue->submissions, &submit_unit->entry); - pthread_cond_signal(&queue->submissions_cond); - pthread_mutex_unlock(&queue->submissions_mutex); - - return VK_SUCCESS; -} - VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo2 *submits_orig, VkFence fence, bool khr) { @@ -5144,23 +4549,26 @@ static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, c TRACE("(%p, %u, %p, %s)\n", queue_handle, submit_count, submits, wine_dbgstr_longlong(fence)); - if (is_virtual_queue(queue)) - return virtual_queue_submit2(queue, submit_count, submits, fence, khr); - for (i = 0; i < submit_count; i++) { for (k = 0; k < submits[i].waitSemaphoreInfoCount; k++) { if (wine_semaphore_from_handle(submits[i].pWaitSemaphoreInfos[k].semaphore)->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - return virtual_queue_submit2(queue, submit_count, submits, fence, khr); + { + FIXME("Queue submissions with waits on D3D12-Fence compatible timeline semaphores not supported.\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } } for (k = 0; k < submits[i].signalSemaphoreInfoCount; k++) { if (wine_semaphore_from_handle(submits[i].pSignalSemaphoreInfos[k].semaphore)->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - return virtual_queue_submit2(queue, submit_count, submits, fence, khr); + { + FIXME("Queue submissions signalling D3D12-Fence compatible timeline semaphores not supported.\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } } } @@ -5179,7 +4587,6 @@ VkResult wine_vkQueueSubmit2KHR(VkQueue queue, uint32_t submit_count, const VkSu VkResult wine_vkQueuePresentKHR(VkQueue queue_handle, const VkPresentInfoKHR *present_info) { - struct wine_queue *queue = wine_queue_from_handle(queue_handle); VkPresentInfoKHR host_present_info = *present_info; struct wine_semaphore *semaphore; struct conversion_context ctx; @@ -5199,14 +4606,6 @@ VkResult wine_vkQueuePresentKHR(VkQueue queue_handle, const VkPresentInfoKHR *pr } } - if (is_virtual_queue(queue)) - { - pthread_mutex_lock(&queue->submissions_mutex); - while (queue->processing) - pthread_cond_wait(&queue->submissions_cond, &queue->submissions_mutex); - pthread_mutex_unlock(&queue->submissions_mutex); - } - init_conversion_context(&ctx); host_present_info.pWaitSemaphores = unwrap_semaphore_array(present_info->pWaitSemaphores, present_info->waitSemaphoreCount, &ctx); ret = fshack_vk_queue_present(queue_handle, &host_present_info); @@ -5225,15 +4624,6 @@ VkResult wine_vkQueueBindSparse(VkQueue queue_handle, uint32_t bind_info_count, TRACE("(%p, %u, %p, 0x%s)\n", queue, bind_info_count, bind_info, wine_dbgstr_longlong(fence)); - if (is_virtual_queue(queue)) - { - FIXME("Can't process sparse bind calls on virtual queue, flushing.\n"); - pthread_mutex_lock(&queue->submissions_mutex); - while (queue->processing) - pthread_cond_wait(&queue->submissions_cond, &queue->submissions_mutex); - pthread_mutex_unlock(&queue->submissions_mutex); - } - for (i = 0; i < bind_info_count; i++) { batch = (VkBindSparseInfo *)&bind_info[i]; @@ -5302,20 +4692,3 @@ VkResult wine_vkQueueBindSparse(VkQueue queue_handle, uint32_t bind_info_count, free_conversion_context(&ctx); return ret; } - -VkResult wine_vkQueueWaitIdle(VkQueue queue_handle) -{ - struct wine_queue *queue = wine_queue_from_handle(queue_handle); - - TRACE("(%p)\n", queue); - - if (is_virtual_queue(queue)) - { - pthread_mutex_lock(&queue->submissions_mutex); - while (queue->processing) - pthread_cond_wait(&queue->submissions_cond, &queue->submissions_mutex); - pthread_mutex_unlock(&queue->submissions_mutex); - } - - return queue->device->funcs.p_vkQueueWaitIdle(queue->queue); -} diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 218c2be9efe..aac1c65050d 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -142,7 +142,6 @@ struct wine_instance */ struct wine_phys_dev **phys_devs; uint32_t phys_dev_count; - uint32_t api_version; VkBool32 enable_wrapper_list; struct list wrappers; @@ -173,7 +172,6 @@ struct wine_phys_dev VkPhysicalDeviceMemoryProperties memory_properties; VkExtensionProperties *extensions; uint32_t extension_count; - uint32_t api_version; uint32_t external_memory_align; @@ -196,22 +194,6 @@ struct wine_queue uint32_t queue_index; VkDeviceQueueCreateFlags flags; - bool virtual_queue; - bool processing; - bool device_lost; - - pthread_t virtual_queue_thread; - pthread_mutex_t submissions_mutex; - pthread_cond_t submissions_cond; - struct list submissions; - - pthread_t signal_thread; - pthread_mutex_t signaller_mutex; - pthread_cond_t signaller_cond; - struct list signal_ops; - - bool stop; - struct wine_vk_mapping mapping; }; From 2b4c582efcfedbf386622265181dce8a9def7300 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 18:45:34 -0600 Subject: [PATCH 698/758] Revert "winevulkan: Implement vkWaitForFences and vkSignalSemaphore for D3D12-Fence compatible timeline semaphores." This reverts commit 6d2a0863d60e67825a30c7e735b68e5c01f48fb3. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 276 +------------------------------ dlls/winevulkan/vulkan_private.h | 20 +-- 2 files changed, 7 insertions(+), 289 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index d0b90293344..ac683720673 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -26,9 +26,6 @@ #include #include #include -#include -#include -#include #include "ntstatus.h" #define WIN32_NO_STATUS @@ -3697,108 +3694,6 @@ static void d3d12_semaphore_unlock(struct wine_semaphore *semaphore) pthread_mutex_unlock(&semaphore->d3d12_fence_shm->mutex); } -/* returns -1 when there is no queued update that would satisfy the wait */ -static uint64_t d3d12_semaphore_try_get_wait_value_locked(struct wine_semaphore *semaphore, uint64_t virtual_value, - struct VkQueue_T *waiting_queue) -{ - struct pending_update *update; - uint64_t ret = -1; - unsigned int i; - - if (semaphore->d3d12_fence_shm->virtual_value >= virtual_value) - return 0; - - return ret; -} - -static struct pending_wait *d3d12_semaphore_push_wait_locked(struct wine_semaphore *semaphore, uint64_t virtual_value) -{ - struct pending_wait *wait; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(semaphore->d3d12_fence_shm->pending_waits); i++) - { - wait = &semaphore->d3d12_fence_shm->pending_waits[i]; - if (!wait->present) - break; - } - - if (i == ARRAY_SIZE(semaphore->d3d12_fence_shm->pending_waits)) - { - FIXME("Failed to wait on semaphore %p, maximum waits exceeded.\n", semaphore); - return NULL; - } - - wait->present = true; - wait->satisfied = false; - wait->virtual_value = virtual_value; - wait->physical_value = 0; - - return wait; -} - -static uint64_t d3d12_semaphore_pop_wait_locked(struct wine_semaphore *semaphore, struct pending_wait *wait) -{ - wait->satisfied = false; - wait->present = false; - - return wait->physical_value; -} - -static void d3d12_semaphore_satisfy_waits_locked(struct wine_semaphore *semaphore, uint64_t virtual_value, - uint64_t physical_value) -{ - struct pending_wait *wait; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(semaphore->d3d12_fence_shm->pending_waits); i++) - { - wait = &semaphore->d3d12_fence_shm->pending_waits[i]; - - if (wait->present && !wait->satisfied && wait->virtual_value <= virtual_value) - { - wait->satisfied = true; - wait->physical_value = physical_value; - pthread_cond_signal(&wait->cond); - } - } -} - -static bool d3d12_semaphore_pop_pending_signal_locked(struct wine_semaphore *semaphore, uint64_t phys_val, struct pending_update *ret) -{ - struct pending_update *update; - unsigned int i; - - for (i = 0; i < semaphore->d3d12_fence_shm->pending_updates_count; i++) - { - if (semaphore->d3d12_fence_shm->pending_updates[i].physical_value == phys_val) - { - update = &semaphore->d3d12_fence_shm->pending_updates[i]; - if (ret) - *ret = *update; - *update = semaphore->d3d12_fence_shm->pending_updates[--semaphore->d3d12_fence_shm->pending_updates_count]; - return true; - } - } - - return false; -} - -static void d3d12_semaphore_update_phys_val_locked(struct wine_semaphore *sem, uint64_t phys_val) -{ - struct pending_update pending; - - /* Based off linked VKD3D-Proton implementation, but we don't signal CPU waits here. - * https://github.com/HansKristian-Work/vkd3d-proton/blob/829ac72e3d381006a843c183e613e8ee77e0b292/libs/vkd3d/command.c#L758 */ - while (sem->d3d12_fence_shm->physical_value < phys_val) - { - sem->d3d12_fence_shm->physical_value++; - - if (d3d12_semaphore_pop_pending_signal_locked(sem, sem->d3d12_fence_shm->physical_value, &pending)) - sem->d3d12_fence_shm->virtual_value = pending.virtual_value; - } -} - VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkSemaphore *semaphore, void *win_create_info) { @@ -3811,11 +3706,9 @@ VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateI VkSemaphoreGetFdInfoKHR fd_info; pthread_mutexattr_t mutex_attr; struct wine_semaphore *object; - pthread_condattr_t cond_attr; OBJECT_ATTRIBUTES attr; HANDLE section_handle; LARGE_INTEGER li; - unsigned int i; VkResult res; SIZE_T size; int fd; @@ -3937,14 +3830,6 @@ VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateI } pthread_mutexattr_destroy(&mutex_attr); - for (i = 0; i < ARRAY_SIZE(object->d3d12_fence_shm->pending_waits); i++) - { - pthread_condattr_init(&cond_attr); - pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); - pthread_cond_init(&object->d3d12_fence_shm->pending_waits[i].cond, &cond_attr); - pthread_condattr_destroy(&cond_attr); - } - WINE_VK_ADD_NON_DISPATCHABLE_MAPPING(device->phys_dev->instance, object, object->fence_timeline_semaphore, object); } if (object->fence_timeline_semaphore == VK_NULL_HANDLE) @@ -4196,41 +4081,14 @@ static VkResult vk_signal_semaphore(VkDevice device_handle, const VkSemaphoreSig static NTSTATUS wine_vk_signal_semaphore(VkDevice device, const VkSemaphoreSignalInfo *signal_info, bool khr) { - uint64_t phys_val; - VkResult vr; - struct wine_semaphore *semaphore = wine_semaphore_from_handle(signal_info->semaphore); TRACE("(%p, %p)\n", device, signal_info); if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) { - VkSemaphoreSignalInfo step_signal_info; - - d3d12_semaphore_lock(semaphore); - - assert(semaphore->d3d12_fence_shm->counter == phys_val); - phys_val++; - - semaphore->d3d12_fence_shm->counter = semaphore->d3d12_fence_shm->physical_value = phys_val; - - step_signal_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO; - step_signal_info.pNext = NULL; - step_signal_info.semaphore = signal_info->semaphore; - step_signal_info.value = phys_val; - - vr = vk_signal_semaphore(device, &step_signal_info, khr); - if (vr != VK_SUCCESS) - { - d3d12_semaphore_unlock(semaphore); - return vr; - } - - d3d12_semaphore_satisfy_waits_locked(semaphore, signal_info->value, 0); - semaphore->d3d12_fence_shm->virtual_value = signal_info->value; - - d3d12_semaphore_unlock(semaphore); - return VK_SUCCESS; + FIXME("Signalling D3D12-Fence compatible timeline semaphore not supported.\n"); + return VK_ERROR_OUT_OF_HOST_MEMORY; } return vk_signal_semaphore(device, signal_info, khr); @@ -4278,143 +4136,21 @@ static VkResult vk_wait_semaphores(struct wine_device *device, const VkSemaphore static VkResult wine_vk_wait_semaphores(VkDevice device_handle, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout, bool khr) { - VkSemaphoreWaitInfo wait_info_dup = *wait_info; - struct timespec abs_timeout, start_time; - struct pending_wait **pending_waits; - struct pending_wait *pending_wait; - unsigned int i, remaining_waits; - VkSemaphore* semaphores_dup; - uint64_t *values_dup; - uint64_t phys_val; - int wait_stat; - VkResult res; + unsigned int i; TRACE("(%p, %p, 0x%s)\n", device_handle, wait_info, wine_dbgstr_longlong(timeout)); - if (timeout) - { - clock_gettime(CLOCK_REALTIME, &start_time); - - abs_timeout.tv_sec = start_time.tv_sec + (timeout / NANOSECONDS_IN_A_SECOND); - abs_timeout.tv_nsec = start_time.tv_nsec + (timeout % NANOSECONDS_IN_A_SECOND); - if (abs_timeout.tv_nsec >= NANOSECONDS_IN_A_SECOND) - { - abs_timeout.tv_sec++; - abs_timeout.tv_nsec-=NANOSECONDS_IN_A_SECOND; - } - } - - wait_info_dup.pSemaphores = semaphores_dup = calloc(wait_info->semaphoreCount, sizeof(VkSemaphore)); - wait_info_dup.pValues = values_dup = calloc(wait_info->semaphoreCount, sizeof(uint64_t)); - pending_waits = calloc(wait_info->semaphoreCount, sizeof(struct pending_wait *)); - for (i = 0; i < wait_info->semaphoreCount; i++) { struct wine_semaphore *semaphore = wine_semaphore_from_handle(wait_info->pSemaphores[i]); - semaphores_dup[i] = wait_info->pSemaphores[i]; - values_dup[i] = wait_info->pValues[i]; - if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) { - d3d12_semaphore_lock(semaphore); - if ((values_dup[i] = d3d12_semaphore_try_get_wait_value_locked(semaphore, wait_info->pValues[i], NULL)) == -1) - { - if (!timeout) - { - d3d12_semaphore_unlock(semaphore); - continue; - } - - pending_wait = d3d12_semaphore_push_wait_locked(semaphore, wait_info->pValues[i]); - - if (wait_info->flags & VK_SEMAPHORE_WAIT_ANY_BIT) - { - /* Keep scheduling a wait of current physical_value+1 until the desired virtual value is signaled */ - values_dup[i] = semaphore->d3d12_fence_shm->physical_value + 1; - pending_waits[i] = pending_wait; - } - else - { - while (!pending_wait->satisfied && wait_stat != ETIMEDOUT) - wait_stat = pthread_cond_timedwait(&pending_wait->cond, &semaphore->d3d12_fence_shm->mutex, &abs_timeout); - - values_dup[i] = d3d12_semaphore_pop_wait_locked(semaphore, pending_wait); - - if (wait_stat == ETIMEDOUT) - { - d3d12_semaphore_unlock(semaphore); - free(semaphores_dup); - free(values_dup); - free(pending_waits); - return VK_TIMEOUT; - } - } - } - d3d12_semaphore_unlock(semaphore); - } - } - do - { - if (timeout) - { - clock_gettime(CLOCK_REALTIME, &start_time); - - if (start_time.tv_sec > abs_timeout.tv_sec || - (start_time.tv_sec == abs_timeout.tv_sec && start_time.tv_nsec >= abs_timeout.tv_nsec)) - timeout = 0; - else - timeout = ((abs_timeout.tv_sec - start_time.tv_sec) * NANOSECONDS_IN_A_SECOND) + - (abs_timeout.tv_nsec - start_time.tv_nsec); - } - - remaining_waits = 0; - res = vk_wait_semaphores(wine_device_from_handle(device_handle), &wait_info_dup, timeout, khr); - - for (i = 0; i < wait_info->semaphoreCount; i++) - { - struct wine_semaphore * semaphore = wine_semaphore_from_handle(wait_info->pSemaphores[i]); - - if (pending_waits[i]) - { - remaining_waits++; - - d3d12_semaphore_lock(semaphore); - if (res != VK_SUCCESS || pending_waits[i]->satisfied) - { - values_dup[i] = pending_waits[i]->physical_value; - d3d12_semaphore_pop_wait_locked(semaphore, pending_waits[i]); - pending_waits[i] = NULL; - } - d3d12_semaphore_unlock(semaphore); - } - } - } - while (res == VK_SUCCESS && remaining_waits); - - /* Make sure the physical value we waited on is processed before returning */ - for (i = 0; i < wait_info_dup.semaphoreCount; i++) - { - struct wine_semaphore *semaphore = wine_semaphore_from_handle(wait_info_dup.pSemaphores[i]); - - if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - d3d12_semaphore_lock(semaphore); - if (wait_info->flags & VK_SEMAPHORE_WAIT_ANY_BIT) - { - if (!vk_get_semaphore_counter_value(device_handle, semaphores_dup[i], &phys_val, khr)) - d3d12_semaphore_update_phys_val_locked(semaphore, phys_val); - } - else - d3d12_semaphore_update_phys_val_locked(semaphore, values_dup[i]); - d3d12_semaphore_unlock(semaphore); + FIXME("Waiting on D3D12-Fence compatible timeline semaphores not supported."); + return VK_ERROR_OUT_OF_HOST_MEMORY; } } - - free(semaphores_dup); - free(values_dup); - free(pending_waits); - return res; + return vk_wait_semaphores(wine_device_from_handle(device_handle), wait_info, timeout, khr); } VkResult wine_vkWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout) diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index aac1c65050d..d5b86388263 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -24,7 +24,6 @@ #define VK_NO_PROTOTYPES #include -#include #include "vulkan_loader.h" #include "vulkan_thunks.h" @@ -337,24 +336,7 @@ struct wine_semaphore struct { pthread_mutex_t mutex; - uint64_t virtual_value, physical_value, counter; - - struct pending_wait - { - bool present, satisfied; - uint64_t virtual_value; - uint64_t physical_value; - pthread_cond_t cond; - } pending_waits[100]; - - struct pending_update - { - uint64_t virtual_value; - uint64_t physical_value; - pid_t signalling_pid; - struct wine_queue *signalling_queue; - } pending_updates[100]; - uint32_t pending_updates_count; + uint64_t virtual_value; } *d3d12_fence_shm; }; From 692d4f3455a39fb148a57f8ad75fe958c13d0425 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Dec 2022 17:59:06 -0600 Subject: [PATCH 699/758] winevulkan: Only append VK_KHR_timeline_semaphore for Vk API version < 1.2. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 27 +++++++++++++++++++++++++++ dlls/winevulkan/vulkan_private.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index ac683720673..faf6c39d888 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -239,6 +239,7 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance struct wine_phys_dev *object; uint32_t num_host_properties, num_properties = 0; VkExtensionProperties *host_properties = NULL; + VkPhysicalDeviceProperties physdev_properties; BOOL have_external_memory_host = FALSE; VkResult res; unsigned int i, j; @@ -250,6 +251,9 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance object->handle = handle; object->phys_dev = phys_dev; + instance->funcs.p_vkGetPhysicalDeviceProperties(phys_dev, &physdev_properties); + object->api_version = physdev_properties.apiVersion; + handle->base.unix_handle = (uintptr_t)object; WINE_VK_ADD_DISPATCHABLE_MAPPING(instance, handle, phys_dev, object); @@ -503,6 +507,8 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de else if (!strcmp(extension_name, "VK_KHR_timeline_semaphore")) append_timeline = 0; } + if (append_timeline) + append_timeline = phys_dev->api_version < VK_API_VERSION_1_2 || phys_dev->instance->api_version < VK_API_VERSION_1_2; if (append_timeline) { append_timeline = 0; @@ -1054,6 +1060,8 @@ VkResult wine_vkCreateInstance(const VkInstanceCreateInfo *create_info, app_info->engineVersion); TRACE("API version %#x.\n", app_info->apiVersion); + object->api_version = app_info->apiVersion; + if (app_info->pEngineName && !strcmp(app_info->pEngineName, "idTech")) object->quirks |= WINEVULKAN_QUIRK_GET_DEVICE_PROC_ADDR; } @@ -1700,6 +1708,25 @@ static void wine_vk_get_physical_device_external_semaphore_properties(struct win break; case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT: { + unsigned int i; + + if (phys_dev->api_version < VK_API_VERSION_1_2 || + phys_dev->instance->api_version < VK_API_VERSION_1_2) + { + for (i = 0; i < phys_dev->extension_count; i++) + { + if (!strcmp(phys_dev->extensions[i].extensionName, "VK_KHR_timeline_semaphore")) + break; + } + if (i == phys_dev->extension_count) + { + properties->exportFromImportedHandleTypes = 0; + properties->compatibleHandleTypes = 0; + properties->externalSemaphoreFeatures = 0; + return; + } + } + if ((p_semaphore_type_info = wine_vk_find_struct(&semaphore_info_dup, SEMAPHORE_TYPE_CREATE_INFO))) { p_semaphore_type_info->semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index d5b86388263..f89787eb67c 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -141,6 +141,7 @@ struct wine_instance */ struct wine_phys_dev **phys_devs; uint32_t phys_dev_count; + uint32_t api_version; VkBool32 enable_wrapper_list; struct list wrappers; @@ -171,6 +172,7 @@ struct wine_phys_dev VkPhysicalDeviceMemoryProperties memory_properties; VkExtensionProperties *extensions; uint32_t extension_count; + uint32_t api_version; uint32_t external_memory_align; From 8875b6a1f01ecb6faebb6e6b4ec96e141b24c31f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Aug 2023 19:34:16 -0600 Subject: [PATCH 700/758] winevulkan: Force copying VkSubmitInfo[2] structures. Based on patches by Derek Lesho. CW-Bug-Id: #22526 --- dlls/winevulkan/make_vulkan | 51 ++++++++++++++++++++++++------------- dlls/winevulkan/vulkan.c | 2 +- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index b7d57f2964e..1332875e3ef 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -220,7 +220,7 @@ FUNCTION_OVERRIDES = { "vkSignalSemaphore" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkWaitSemaphores" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueBindSparse" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, - "vkQueueSubmit" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, + "vkQueueSubmit" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, "vkQueueSubmit2" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, # VK_KHR_surface @@ -311,11 +311,16 @@ STRUCT_CHAIN_CONVERSIONS = { "VkPhysicalDeviceImageFormatInfo2": [], "VkPhysicalDeviceExternalSemaphoreInfo": [], "VkSemaphoreCreateInfo": ["VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR"], - "VkSubmitInfo": [], + "VkSubmitInfo": ["VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR"], "VkSubmitInfo2": [], "VkBindSparseInfo" : [], } +STRUCT_COPY = { + "VkSubmitInfo", + "VkSubmitInfo2", +}; + # Some struct members are conditionally ignored and callers are free to leave them uninitialized. # We can't deduce that from XML, so we allow expressing it here. MEMBER_LENGTH_EXPRESSIONS = { @@ -1403,6 +1408,9 @@ class VkVariable(object): if struct.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const): conversions.append(StructConversionFunction(struct, Direction.OUTPUT, conv, unwrap, is_const)) + if struct.name in STRUCT_COPY: + conversions.append(StructConversionFunction(struct, Direction.INPUT, False, unwrap, is_const, True)) + if self.is_static_array() or self.is_dynamic_array(): for conv in [False, True]: if self.needs_conversion(conv, unwrap, Direction.INPUT, parent_const): @@ -1532,7 +1540,7 @@ class VkMember(VkVariable): values=values, object_type=object_type, bit_width=bit_width, returnedonly=returnedonly, parent=parent, selection=selection, selector=selector) - def copy(self, input, output, direction, conv, unwrap): + def copy(self, input, output, direction, conv, unwrap, copy): """ Helper method for use by conversion logic to generate a C-code statement to copy this member. - `conv` indicates whether the statement is in a struct alignment conversion path. """ @@ -1590,6 +1598,8 @@ class VkMember(VkVariable): elif self.is_static_array(): bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type) return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count) + elif self.is_dynamic_array() and copy: + return "MEMDUP(ctx, {0}{1}, {2}{1}, {3});\n".format(output, self.name, input, self.get_dyn_array_len(input, conv)) elif direction == Direction.INPUT: return "{0}{1} = {2};\n".format(output, self.name, self.value(input, conv)) elif conv and direction == Direction.OUTPUT and self.is_pointer(): @@ -2312,21 +2322,25 @@ class VkStruct(Sequence): class StructConversionFunction(object): - def __init__(self, struct, direction, conv, unwrap, const): + def __init__(self, struct, direction, conv, unwrap, const, copy=False): self.direction = direction self.operand = struct self.type = struct.name self.conv = conv self.unwrap = unwrap or not self.operand.needs_unwrapping() self.const = const + self.copy = copy - name = "convert_{0}_".format(self.type) - win_type = "win32" if self.conv else "win64" - host_part = "host" if self.unwrap else "unwrapped_host" - if self.direction == Direction.INPUT: - name += "{0}_to_{1}".format(win_type, host_part) - else: # Direction.OUTPUT - name += "{0}_to_{1}".format(host_part, win_type) + if copy: + name = "copy_{0}".format(self.type) + else: + name = "convert_{0}_".format(self.type) + win_type = "win32" if self.conv else "win64" + host_part = "host" if self.unwrap else "unwrapped_host" + if self.direction == Direction.INPUT: + name += "{0}_to_{1}".format(win_type, host_part) + else: # Direction.OUTPUT + name += "{0}_to_{1}".format(host_part, win_type) self.name = name def __eq__(self, other): @@ -2358,7 +2372,7 @@ class StructConversionFunction(object): body = "" - if not self.conv: + if not self.conv and not self.copy: body += "#ifdef _WIN64\n" needs_alloc = self.direction != Direction.OUTPUT and self.operand.needs_alloc(self.conv, self.unwrap) @@ -2368,8 +2382,11 @@ class StructConversionFunction(object): if self.direction == Direction.OUTPUT and self.const: win_type = "const " + win_type - if self.conv: + if self.copy: + body += "void {0}(".format(self.name) + else: body += "static inline void {0}(".format(self.name) + if self.conv: if self.direction == Direction.OUTPUT: params = ["const {0} *in".format(self.type), "{0} *out".format(win_type)] @@ -2386,8 +2403,6 @@ class StructConversionFunction(object): body += ")\n" else: - body += "static inline void {0}(".format(self.name) - params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)] # Generate parameter list @@ -2427,7 +2442,7 @@ class StructConversionFunction(object): body += " || ".join("selector == {}".format(s) for s in m.selection) body += ")\n " - body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap) + body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap, self.copy) if needs_extensions: if self.conv and self.direction == Direction.INPUT: @@ -2487,7 +2502,7 @@ class StructConversionFunction(object): copy_body += ident + "out_ext->pNext = NULL;\n" continue - copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True) + copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True, self.copy) # Generate the definition of "in_ext" if we need it if "in_ext->" in copy_body: @@ -2522,7 +2537,7 @@ class StructConversionFunction(object): body += " FIXME(\"Unexpected pNext\\n\");\n" body += "}\n" - if not self.conv: + if not self.conv and not self.copy: body += "#endif /* _WIN64 */\n" body += "\n" diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index faf6c39d888..a755600813e 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -4218,7 +4218,7 @@ VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, return ret; } -VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits, VkFence fence) +VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits, VkFence fence, void *submits_win_ptr) { struct wine_queue *queue = wine_queue_from_handle(queue_handle); unsigned int i, k; From 4a5495f12434833a10ea346a6bd0b05ddf9f78c2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 31 Jul 2023 11:12:21 -0600 Subject: [PATCH 701/758] winevulkan: Support waiting for and signalling d3d12 shared fences. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 676 +++++++++++++++++++++++++------ dlls/winevulkan/vulkan_private.h | 39 +- 2 files changed, 578 insertions(+), 137 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index a755600813e..d5254629ec9 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -26,6 +26,13 @@ #include #include #include +#include +#include +#include +#include +#ifdef HAVE_SYS_SYSCALL_H +# include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -33,6 +40,7 @@ #include "winnt.h" #include "winioctl.h" #include "wine/server.h" +#include "wine/list.h" #include "vulkan_private.h" #include "wine/vulkan_driver.h" @@ -40,6 +48,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(vulkan); +static int debug_level; static BOOL is_wow64(void) { @@ -120,6 +129,21 @@ static uint64_t wine_vk_get_wrapper(struct wine_instance *instance, uint64_t nat return result; } +static void signal_timeline_sem(struct wine_device *device, VkSemaphore sem, uint64_t *value) +{ + /* May be called from native thread. */ + struct VkSemaphoreSignalInfo info = { 0 }; + VkResult res; + + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO; + info.semaphore = sem; + ++*value; + info.value = *value; + res = device->funcs.p_vkSignalSemaphore(device->device, &info); + if (res != VK_SUCCESS) + fprintf(stderr, "err:winevulkan:signal_timeline_sem vkSignalSemaphore failed, res=%d.\n", res); +} + static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, @@ -574,11 +598,32 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de */ static void wine_vk_device_free(struct wine_device *device) { + struct pending_d3d12_fence_op *op; struct wine_queue *queue; if (!device) return; + if (device->signaller_thread) + { + TRACE("Shutting down signaller thread.\n"); + pthread_mutex_lock(&device->signaller_mutex); + device->stop = 1; + signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); + pthread_mutex_unlock(&device->signaller_mutex); + pthread_join(device->signaller_thread, NULL); + device->funcs.p_vkDestroySemaphore(device->device, device->sem_poll_update.sem, NULL); + pthread_cond_destroy(&device->sem_poll_updated_cond); + TRACE("Signaller thread shut down.\n"); + } + pthread_mutex_destroy(&device->signaller_mutex); + + LIST_FOR_EACH_ENTRY(op, &device->free_fence_ops_list, struct pending_d3d12_fence_op, entry) + { + device->funcs.p_vkDestroySemaphore(device->device, op->local_sem.sem, NULL); + free(op); + } + if (device->queues) { unsigned int i; @@ -892,6 +937,9 @@ VkResult wine_vkCreateDevice(VkPhysicalDevice phys_dev_handle, const VkDeviceCre if (!(object = calloc(1, sizeof(*object)))) return VK_ERROR_OUT_OF_HOST_MEMORY; + pthread_mutex_init(&object->signaller_mutex, NULL); + list_init(&object->sem_poll_list); + list_init(&object->free_fence_ops_list); object->phys_dev = phys_dev; if ((callback = (VkCreateInfoWineDeviceCallback *)create_info->pNext) @@ -3721,6 +3769,340 @@ static void d3d12_semaphore_unlock(struct wine_semaphore *semaphore) pthread_mutex_unlock(&semaphore->d3d12_fence_shm->mutex); } +static VkSemaphore create_timeline_semaphore(struct wine_device *device) +{ + VkSemaphoreTypeCreateInfo timeline_info = { 0 }; + VkSemaphoreCreateInfo create_info = { 0 }; + VkSemaphore sem = 0; + VkResult res; + + timeline_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + timeline_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + create_info.pNext = &timeline_info; + + res = device->funcs.p_vkCreateSemaphore(device->device, &create_info, NULL, &sem); + if (res != VK_SUCCESS) + ERR("vkCreateSemaphore failed, res=%d\n", res); + return sem; +} + +static void release_fence_op(struct wine_device *device, struct pending_d3d12_fence_op *op) +{ + list_remove(&op->entry); + WINE_VK_REMOVE_HANDLE_MAPPING(device->phys_dev->instance, op); + list_add_head(&device->free_fence_ops_list, &op->entry); +} + +static int wait_info_realloc(VkSemaphoreWaitInfo *wait_info, uint32_t *wait_alloc_count) +{ + VkSemaphore *new_sem; + uint64_t *new_values; + + if (wait_info->semaphoreCount + 1 <= *wait_alloc_count) + return 1; + new_sem = realloc((void *)wait_info->pSemaphores, *wait_alloc_count * 2 * sizeof(*new_sem)); + if (!new_sem) + { + fprintf(stderr, "err:winevulkan:wait_info_realloc no memory.\n"); + return 0; + } + new_values = realloc((void *)wait_info->pValues, *wait_alloc_count * 2 * sizeof(*new_values)); + if (!new_values) + { + fprintf(stderr, "err:winevulkan:wait_info_realloc no memory.\n"); + return 0; + } + *wait_alloc_count *= 2; + wait_info->pSemaphores = new_sem; + wait_info->pValues = new_values; + return 1; +} + +static int add_sem_wait(VkSemaphoreWaitInfo *wait_info, uint32_t *wait_alloc_count, VkSemaphore sem, uint64_t value) +{ + if (!wait_info_realloc(wait_info, wait_alloc_count)) + return 0; + ((VkSemaphore *)wait_info->pSemaphores)[wait_info->semaphoreCount] = sem; + ((uint64_t *)wait_info->pValues)[wait_info->semaphoreCount] = value; + ++wait_info->semaphoreCount; + return 1; +} + +static int semaphore_process(struct wine_device *device, struct wine_semaphore *sem, + VkSemaphoreWaitInfo *wait_info, uint32_t *wait_alloc_count) +{ + /* Called from native thread. */ + struct pending_d3d12_fence_op *op, *op2; + uint64_t global_sem_wait_value; + int virtual_value_updated = 0; + uint64_t value, virtual_value; + VkResult res; + + /* Check local pending signal ops completion, update shared semaphore. */ + d3d12_semaphore_lock( sem ); + LIST_FOR_EACH_ENTRY_SAFE(op, op2, &sem->pending_signals, struct pending_d3d12_fence_op, entry) + { + if (op->virtual_value <= sem->d3d12_fence_shm->virtual_value) + goto signal_op_complete; + + res = device->funcs.p_vkGetSemaphoreCounterValue(device->device, op->local_sem.sem, &value); + if (res != VK_SUCCESS) + { + fprintf(stderr, "err:winevulkan:semaphore_process vkGetSemaphoreCounterValue failed, res=%d.\n", res); + goto signal_op_complete; + } + if (value <= op->local_sem.value) + { + if (!add_sem_wait(wait_info, wait_alloc_count, op->local_sem.sem, op->local_sem.value + 1)) + { + d3d12_semaphore_unlock(sem); + return 0; + } + continue; + } + + sem->d3d12_fence_shm->virtual_value = op->virtual_value; + virtual_value_updated = 1; +signal_op_complete: + ++op->local_sem.value; + release_fence_op(device, op); + } + + if (virtual_value_updated) + signal_timeline_sem(device, sem->fence_timeline_semaphore, &sem->d3d12_fence_shm->physical_value); + global_sem_wait_value = sem->d3d12_fence_shm->physical_value + 1; + virtual_value = sem->d3d12_fence_shm->virtual_value; + d3d12_semaphore_unlock(sem); + + /* Complete satisfied local waits. */ + LIST_FOR_EACH_ENTRY_SAFE(op, op2, &sem->pending_waits, struct pending_d3d12_fence_op, entry) + { + if (op->virtual_value > virtual_value) + continue; + + signal_timeline_sem(device, op->local_sem.sem, &op->local_sem.value); + release_fence_op(device, op); + } + + /* Only poll shared semaphore if there are waits pending. */ + if (list_empty(&sem->pending_waits)) + return 1; + return add_sem_wait(wait_info, wait_alloc_count, sem->fence_timeline_semaphore, global_sem_wait_value); +} + +#define SIGNALLER_INITIAL_WAIT_COUNT 256 + +void *signaller_worker(void *arg) +{ +#ifdef HAVE_SYS_SYSCALL_H + int unix_tid = syscall( __NR_gettid ); +#else + int unix_tid = -1; +#endif + struct wine_device *device = arg; + struct wine_semaphore *sem; + VkSemaphoreWaitInfo wait_info = { 0 }; + uint32_t wait_alloc_count = 0; + VkResult res; + + if (debug_level) + fprintf(stderr, "[%d] msg:winevulkan:signaller_worker started.\n", unix_tid); + + wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; + wait_info.flags = VK_SEMAPHORE_WAIT_ANY_BIT; + wait_alloc_count = SIGNALLER_INITIAL_WAIT_COUNT; + if (!(wait_info.pSemaphores = malloc(sizeof(*wait_info.pSemaphores) * wait_alloc_count))) + { + fprintf(stderr, "err:winevulkan:signaller_worker no memory.\n"); + return NULL; + } + if (!(wait_info.pValues = malloc(sizeof(*wait_info.pValues) * wait_alloc_count))) + { + fprintf(stderr, "err:winevulkan:signaller_worker no memory.\n"); + free((void *)wait_info.pSemaphores); + return NULL; + } + + for (;;) + { + pthread_mutex_lock(&device->signaller_mutex); + if (device->stop) + { + pthread_mutex_unlock(&device->signaller_mutex); + break; + } + wait_info.semaphoreCount = 1; + *(VkSemaphore *)wait_info.pSemaphores = device->sem_poll_update.sem; + *(uint64_t *)wait_info.pValues = device->sem_poll_update.value + 1; + LIST_FOR_EACH_ENTRY(sem, &device->sem_poll_list, struct wine_semaphore, poll_entry) + { + if (!semaphore_process(device, sem, &wait_info, &wait_alloc_count)) + { + pthread_mutex_unlock(&device->signaller_mutex); + break; + } + } + device->sem_poll_update_value = device->sem_poll_update.value; + pthread_cond_signal(&device->sem_poll_updated_cond); + pthread_mutex_unlock(&device->signaller_mutex); + while ((res = device->funcs.p_vkWaitSemaphores(device->device, &wait_info, 3000000000ull)) == VK_TIMEOUT) + { + if (wait_info.semaphoreCount > 1) + fprintf(stderr, "err:winevulkan:signaller_worker wait timed out with non-empty poll list.\n"); + } + if (res != VK_SUCCESS) + { + fprintf(stderr, "err:winevulkan:signaller_worker error waiting for semaphores, vr %d.\n", res); + break; + } + } + + free((void *)wait_info.pSemaphores); + free((void *)wait_info.pValues); + if (debug_level) + fprintf(stderr, "[%d] msg:winevulkan:signaller_worker exiting.\n", unix_tid); + + return NULL; +} + +static void register_sem_poll(struct wine_device *device, struct wine_semaphore *semaphore) +{ + pthread_mutex_lock(&device->signaller_mutex); + if (!device->signaller_thread) + { + device->sem_poll_update.sem = create_timeline_semaphore(device); + device->sem_poll_update.value = 0; + pthread_cond_init(&device->sem_poll_updated_cond, NULL); + if (TRACE_ON(vulkan)) + debug_level = 4; + else if (WARN_ON(vulkan)) + debug_level = 3; + else if (FIXME_ON(vulkan)) + debug_level = 2; + else if (ERR_ON(vulkan)) + debug_level = 1; + else + debug_level = 0; + if (pthread_create(&device->signaller_thread, NULL, signaller_worker, device)) + ERR("Failed to create signaller_worker.\n"); + WARN("d3d12 fence used, created signaller worker.\n"); + } + assert(!semaphore->poll_entry.next); + list_add_head(&device->sem_poll_list, &semaphore->poll_entry); + signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); + pthread_mutex_unlock(&device->signaller_mutex); +} + +static void update_sem_poll_wait_processed(struct wine_device *device) +{ + uint64_t update_value; + + signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); + update_value = device->sem_poll_update.value; + while (device->sem_poll_update_value < update_value) + pthread_cond_wait(&device->sem_poll_updated_cond, &device->signaller_mutex); +} + +static void unregister_sem_poll(struct wine_device *device, struct wine_semaphore *semaphore) +{ + struct list *entry; + + pthread_mutex_lock(&device->signaller_mutex); + list_remove(&semaphore->poll_entry); + semaphore->poll_entry.next = semaphore->poll_entry.prev = NULL; + update_sem_poll_wait_processed(device); + pthread_mutex_unlock(&device->signaller_mutex); + + while ((entry = list_head(&semaphore->pending_waits))) + release_fence_op(device, CONTAINING_RECORD(entry, struct pending_d3d12_fence_op, entry)); + while ((entry = list_head(&semaphore->pending_signals))) + release_fence_op(device, CONTAINING_RECORD(entry, struct pending_d3d12_fence_op, entry)); +} + +static struct pending_d3d12_fence_op *get_free_fence_op(struct wine_device *device) +{ + struct pending_d3d12_fence_op *op; + struct list *entry; + + if ((entry = list_head(&device->free_fence_ops_list))) + { + list_remove(entry); + return CONTAINING_RECORD(entry, struct pending_d3d12_fence_op, entry); + } + + if (!(op = malloc(sizeof(*op)))) + { + ERR("No memory.\n"); + return NULL; + } + op->local_sem.sem = create_timeline_semaphore(device); + op->local_sem.value = 0; + ++device->allocated_fence_ops_count; + TRACE("Total allocated fence ops %u.\n", device->allocated_fence_ops_count); + return op; +} + +static void add_sem_wait_op(struct wine_device *device, struct wine_semaphore *semaphore, uint64_t virtual_value, + VkSemaphore *phys_semaphore, uint64_t *phys_wait_value) +{ + struct pending_d3d12_fence_op *op; + + pthread_mutex_lock(&device->signaller_mutex); + LIST_FOR_EACH_ENTRY(op, &semaphore->pending_waits, struct pending_d3d12_fence_op, entry) + { + if (op->virtual_value == virtual_value) + { + *phys_semaphore = op->local_sem.sem; + *phys_wait_value = op->local_sem.value + 1; + pthread_mutex_unlock(&device->signaller_mutex); + return; + } + } + if ((op = get_free_fence_op(device))) + { + op->virtual_value = virtual_value; + *phys_semaphore = op->local_sem.sem; + *phys_wait_value = op->local_sem.value + 1; + list_add_tail(&semaphore->pending_waits, &op->entry); + WINE_VK_ADD_NON_DISPATCHABLE_MAPPING(device->phys_dev->instance, semaphore, op->local_sem.sem, op); + signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); + TRACE("added wait op, semaphore %p, %s, temp sem %s, %s.\n", semaphore, wine_dbgstr_longlong(virtual_value), + wine_dbgstr_longlong(op->local_sem.sem), wine_dbgstr_longlong(op->local_sem.value)); + } + else + { + *phys_semaphore = 0; + *phys_wait_value = 0; + } + pthread_mutex_unlock(&device->signaller_mutex); +} + +static void add_sem_signal_op(struct wine_device *device, struct wine_semaphore *semaphore, uint64_t virtual_value, + VkSemaphore *phys_semaphore, uint64_t *phys_signal_value) +{ + struct pending_d3d12_fence_op *op; + + pthread_mutex_lock(&device->signaller_mutex); + if ((op = get_free_fence_op(device))) + { + op->virtual_value = virtual_value; + *phys_semaphore = op->local_sem.sem; + *phys_signal_value = op->local_sem.value + 1; + list_add_tail(&semaphore->pending_signals, &op->entry); + WINE_VK_ADD_NON_DISPATCHABLE_MAPPING(device->phys_dev->instance, semaphore, op->local_sem.sem, op); + signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); + TRACE("added signal op, semaphore %p, %s, temp sem %s, %s.\n", semaphore, wine_dbgstr_longlong(virtual_value), + wine_dbgstr_longlong(op->local_sem.sem), wine_dbgstr_longlong(op->local_sem.value)); + } + else + { + *phys_semaphore = 0; + *phys_signal_value = 0; + } + pthread_mutex_unlock(&device->signaller_mutex); +} + VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkSemaphore *semaphore, void *win_create_info) { @@ -3748,6 +4130,9 @@ VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateI if (!(object = calloc(1, sizeof(*object)))) return VK_ERROR_OUT_OF_HOST_MEMORY; + list_init(&object->pending_signals); + list_init(&object->pending_waits); + object->handle = INVALID_HANDLE_VALUE; if ((export_semaphore_info = wine_vk_find_struct(&create_info_dup, EXPORT_SEMAPHORE_CREATE_INFO))) @@ -3880,6 +4265,12 @@ VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateI device->funcs.p_vkDestroySemaphore(device->device, object->fence_timeline_semaphore, NULL); free(object); } + else if (object->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) + register_sem_poll(device, object); + if (res == VK_SUCCESS) + { + TRACE("-> %p (native %#llx, shared %#llx).\n", object, (long long)object->semaphore, (long long)object->fence_timeline_semaphore); + } return res; } @@ -3911,6 +4302,9 @@ void wine_vkDestroySemaphore(VkDevice device_handle, VkSemaphore semaphore_handl if (!semaphore) return; + if (semaphore->poll_entry.next) + unregister_sem_poll(device, semaphore); + if (semaphore->handle != INVALID_HANDLE_VALUE) NtClose(semaphore->handle); @@ -3942,6 +4336,9 @@ VkResult wine_vkImportSemaphoreWin32HandleKHR(VkDevice device_handle, TRACE("(%p, %p). semaphore = %p handle = %p\n", device, handle_info, semaphore, handle_info->handle); + if (semaphore->poll_entry.next) + unregister_sem_poll(device, semaphore); + if (handle_info->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT && !semaphore->fence_timeline_semaphore) { type_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; @@ -4042,6 +4439,9 @@ VkResult wine_vkImportSemaphoreWin32HandleKHR(VkDevice device_handle, NtUnmapViewOfSection(GetCurrentProcess(), semaphore->d3d12_fence_shm); *semaphore = output_semaphore; + assert(!semaphore->poll_entry.next); + if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) + register_sem_poll(device, semaphore); } else { @@ -4057,20 +4457,10 @@ VkResult wine_vkImportSemaphoreWin32HandleKHR(VkDevice device_handle, return res; } -static VkResult vk_get_semaphore_counter_value(VkDevice device_handle, VkSemaphore semaphore_handle, uint64_t *value, bool khr) -{ - struct wine_semaphore *semaphore = wine_semaphore_from_handle(semaphore_handle); - struct wine_device *device = wine_device_from_handle(device_handle); - - if (khr) - return device->funcs.p_vkGetSemaphoreCounterValueKHR(device->device, wine_semaphore_host_handle(semaphore), value); - else - return device->funcs.p_vkGetSemaphoreCounterValue(device->device, wine_semaphore_host_handle(semaphore), value); -} - static VkResult wine_vk_get_semaphore_counter_value(VkDevice device_handle, VkSemaphore semaphore_handle, uint64_t *value, bool khr) { struct wine_semaphore *semaphore = wine_semaphore_from_handle(semaphore_handle); + struct wine_device *device = wine_device_from_handle(device_handle); if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) { @@ -4080,7 +4470,10 @@ static VkResult wine_vk_get_semaphore_counter_value(VkDevice device_handle, VkSe return VK_SUCCESS; } - return vk_get_semaphore_counter_value(device_handle, semaphore_handle, value, khr); + if (khr) + return device->funcs.p_vkGetSemaphoreCounterValueKHR(device->device, wine_semaphore_host_handle(semaphore), value); + else + return device->funcs.p_vkGetSemaphoreCounterValue(device->device, wine_semaphore_host_handle(semaphore), value); } VkResult wine_vkGetSemaphoreCounterValue(VkDevice device_handle, VkSemaphore semaphore_handle, uint64_t *value) @@ -4093,116 +4486,191 @@ VkResult wine_vkGetSemaphoreCounterValueKHR(VkDevice device_handle, VkSemaphore return wine_vk_get_semaphore_counter_value(device_handle, semaphore_handle, value, true); } -static VkResult vk_signal_semaphore(VkDevice device_handle, const VkSemaphoreSignalInfo *signal_info, bool khr) +static NTSTATUS wine_vk_signal_semaphore(VkDevice device_handle, const VkSemaphoreSignalInfo *signal_info, bool khr) { struct wine_semaphore *semaphore = wine_semaphore_from_handle(signal_info->semaphore); struct wine_device *device = wine_device_from_handle(device_handle); VkSemaphoreSignalInfo dup_signal_info = *signal_info; - dup_signal_info.semaphore = wine_semaphore_host_handle(semaphore); + TRACE("(%p, %p)\n", device, signal_info); + + if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) + add_sem_signal_op(device, semaphore, signal_info->value, &dup_signal_info.semaphore, &dup_signal_info.value); + else + dup_signal_info.semaphore = wine_semaphore_host_handle(semaphore); + if (khr) return device->funcs.p_vkSignalSemaphoreKHR(device->device, &dup_signal_info); else return device->funcs.p_vkSignalSemaphore(device->device, &dup_signal_info); } -static NTSTATUS wine_vk_signal_semaphore(VkDevice device, const VkSemaphoreSignalInfo *signal_info, bool khr) +VkResult wine_vkSignalSemaphore(VkDevice device_handle, const VkSemaphoreSignalInfo *signal_info) { - struct wine_semaphore *semaphore = wine_semaphore_from_handle(signal_info->semaphore); - - TRACE("(%p, %p)\n", device, signal_info); - - if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - FIXME("Signalling D3D12-Fence compatible timeline semaphore not supported.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - return vk_signal_semaphore(device, signal_info, khr); + return wine_vk_signal_semaphore(device_handle, signal_info, false); } -VkResult wine_vkSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo *signal_info) +VkResult wine_vkSignalSemaphoreKHR(VkDevice device_handle, const VkSemaphoreSignalInfo *signal_info) { - return wine_vk_signal_semaphore(device, signal_info, false); + return wine_vk_signal_semaphore(device_handle, signal_info, true); } -VkResult wine_vkSignalSemaphoreKHR(VkDevice device, const VkSemaphoreSignalInfo *signal_info) +static void unwrap_semaphore(struct wine_device *device, VkSemaphore *sem_handle, uint64_t *value, BOOL signal) { - return wine_vk_signal_semaphore(device, signal_info, true); + struct wine_semaphore *sem = wine_semaphore_from_handle(*sem_handle); + + if (!sem) + return; + + if (sem->handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) + { + *sem_handle = wine_semaphore_host_handle(sem); + return; + } + if (signal) + add_sem_signal_op(device, sem, *value, sem_handle, value); + else + add_sem_wait_op(device, sem, *value, sem_handle, value); } -static VkSemaphore *unwrap_semaphore_array(const VkSemaphore *in, uint32_t count, struct conversion_context *ctx) +static VkResult unwrap_semaphore_array(const VkSemaphore **sems, const uint64_t **values_out, + uint32_t count, struct conversion_context *ctx, BOOL signal, struct wine_device *device) { + const uint64_t *values = NULL; + const VkSemaphore *in; VkSemaphore *out; unsigned int i; - if (!in || !count) return NULL; + in = *sems; + *sems = NULL; + + if (!in || !count) + return VK_SUCCESS; out = conversion_context_alloc(ctx, count * sizeof(*out)); for (i = 0; i < count; ++i) - out[i] = in[i] ? wine_semaphore_host_handle(wine_semaphore_from_handle(in[i])) : VK_NULL_HANDLE; - - return out; + { + struct wine_semaphore *sem; + if (!in[i]) + { + out[i] = VK_NULL_HANDLE; + continue; + } + sem = wine_semaphore_from_handle(in[i]); + if (sem->handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) + { + out[i] = wine_semaphore_host_handle(sem); + continue; + } + if (!values_out) + { + ERR("D3D12 fence without values specified.\n"); + return VK_ERROR_UNKNOWN; + } + if (!values) + { + values = *values_out; + *values_out = conversion_context_alloc(ctx, count * sizeof(*values_out)); + memcpy((void *)*values_out, values, count * sizeof(*values)); + } + if (signal) + add_sem_signal_op(device, sem, values[i], &out[i], (uint64_t *)&(*values_out)[i]); + else + add_sem_wait_op(device, sem, values[i], &out[i], (uint64_t *)&(*values_out)[i]); + } + *sems = out; + return VK_SUCCESS; } -static VkResult vk_wait_semaphores(struct wine_device *device, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout, bool khr) +static VkResult wine_vk_wait_semaphores(VkDevice device_handle, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout, bool khr) { + struct wine_device *device = wine_device_from_handle(device_handle); VkSemaphoreWaitInfo wait_info_dup = *wait_info; struct conversion_context ctx; VkResult ret; init_conversion_context(&ctx); - wait_info_dup.pSemaphores = unwrap_semaphore_array(wait_info->pSemaphores, wait_info->semaphoreCount, &ctx); + if ((ret = unwrap_semaphore_array(&wait_info_dup.pSemaphores, &wait_info_dup.pValues, + wait_info->semaphoreCount, &ctx, FALSE, device))) + goto done; + if (khr) ret = device->funcs.p_vkWaitSemaphoresKHR(device->device, &wait_info_dup, timeout); else ret = device->funcs.p_vkWaitSemaphores(device->device, &wait_info_dup, timeout); +done: free_conversion_context(&ctx); return ret; } -static VkResult wine_vk_wait_semaphores(VkDevice device_handle, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout, bool khr) -{ - unsigned int i; - - TRACE("(%p, %p, 0x%s)\n", device_handle, wait_info, wine_dbgstr_longlong(timeout)); - - for (i = 0; i < wait_info->semaphoreCount; i++) - { - struct wine_semaphore *semaphore = wine_semaphore_from_handle(wait_info->pSemaphores[i]); - - if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - FIXME("Waiting on D3D12-Fence compatible timeline semaphores not supported."); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - return vk_wait_semaphores(wine_device_from_handle(device_handle), wait_info, timeout, khr); -} - VkResult wine_vkWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout) { + TRACE("%p %p %s.\n", device, wait_info, wine_dbgstr_longlong(timeout)); + return wine_vk_wait_semaphores(device, wait_info, timeout, false); } VkResult wine_vkWaitSemaphoresKHR(VkDevice device, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout) { + TRACE("%p %p %s.\n", device, wait_info, wine_dbgstr_longlong(timeout)); + return wine_vk_wait_semaphores(device, wait_info, timeout, true); } -VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo *submits_orig, VkFence fence) +struct struct_chain_def { + VkStructureType sType; + unsigned int size; +}; + +VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits_orig, VkFence fence, + void *submits_win_ptr) +{ + struct wine_queue *queue = wine_queue_from_handle(queue_handle); + struct wine_device *device = queue->device; + VkTimelineSemaphoreSubmitInfo *timeline_submit_info; + const VkSubmitInfo *submits_win = submits_win_ptr; + VkD3D12FenceSubmitInfoKHR *d3d12_submit_info; + const uint64_t **values; struct conversion_context ctx; VkSubmitInfo *submits; unsigned int i, j; VkResult ret; + TRACE("(%p %u %p 0x%s)\n", queue_handle, submit_count, submits_orig, wine_dbgstr_longlong(fence)); + init_conversion_context(&ctx); MEMDUP(&ctx, submits, submits_orig, submit_count); for (i = 0; i < submit_count; ++i) { - submits[i].pWaitSemaphores = unwrap_semaphore_array(submits[i].pWaitSemaphores, submits[i].waitSemaphoreCount, &ctx); - submits[i].pSignalSemaphores = unwrap_semaphore_array(submits[i].pSignalSemaphores, submits[i].signalSemaphoreCount, &ctx); + timeline_submit_info = wine_vk_find_struct(&submits[i], TIMELINE_SEMAPHORE_SUBMIT_INFO); + d3d12_submit_info = wine_vk_find_struct(&submits_win[i], D3D12_FENCE_SUBMIT_INFO_KHR); + if (d3d12_submit_info && !timeline_submit_info) + { + timeline_submit_info = conversion_context_alloc(&ctx, sizeof(*timeline_submit_info)); + timeline_submit_info->sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; + timeline_submit_info->pNext = submits[i].pNext; + timeline_submit_info->waitSemaphoreValueCount = d3d12_submit_info->waitSemaphoreValuesCount; + MEMDUP(&ctx, timeline_submit_info->pWaitSemaphoreValues, d3d12_submit_info->pWaitSemaphoreValues, d3d12_submit_info->waitSemaphoreValuesCount); + timeline_submit_info->signalSemaphoreValueCount = d3d12_submit_info->signalSemaphoreValuesCount; + MEMDUP(&ctx, timeline_submit_info->pSignalSemaphoreValues, d3d12_submit_info->pSignalSemaphoreValues, d3d12_submit_info->signalSemaphoreValuesCount); + submits[i].pNext = &timeline_submit_info; + } + if (d3d12_submit_info && timeline_submit_info) + WARN("Both TIMELINE_SEMAPHORE_SUBMIT_INFO and D3D12_FENCE_SUBMIT_INFO_KHR specified.\n"); + + if (timeline_submit_info) + values = &timeline_submit_info->pWaitSemaphoreValues; + else + values = NULL; + unwrap_semaphore_array(&submits[i].pWaitSemaphores, values, submits[i].waitSemaphoreCount, &ctx, FALSE, device); + + if (timeline_submit_info) + values = &timeline_submit_info->pSignalSemaphoreValues; + else + values = NULL; + unwrap_semaphore_array(&submits[i].pSignalSemaphores, values, submits[i].signalSemaphoreCount, &ctx, TRUE, device); + if (submits[i].pCommandBuffers && submits[i].commandBufferCount) { VkCommandBuffer *out; @@ -4218,38 +4686,6 @@ VkResult vk_queue_submit_unwrap(struct wine_queue *queue, uint32_t submit_count, return ret; } -VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits, VkFence fence, void *submits_win_ptr) -{ - struct wine_queue *queue = wine_queue_from_handle(queue_handle); - unsigned int i, k; - - TRACE("(%p %u %p 0x%s)\n", queue_handle, submit_count, submits, wine_dbgstr_longlong(fence)); - - for (i = 0; i < submit_count; i++) - { - for (k = 0; k < submits[i].waitSemaphoreCount; k++) - { - if (wine_semaphore_from_handle(submits[i].pWaitSemaphores[k])->handle_type == - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - FIXME("Queue submissions with waits on D3D12-Fence compatible timeline semaphores not supported.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - - for (k = 0; k < submits[i].signalSemaphoreCount; k++) - { - if (wine_semaphore_from_handle(submits[i].pSignalSemaphores[k])->handle_type == - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - FIXME("Queue submissions with signalling D3D12-Fence compatible timeline semaphores not supported.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - } - return vk_queue_submit_unwrap(queue, submit_count, submits, fence); -} - static void duplicate_array_for_unwrapping(struct conversion_context *ctx, void **ptr, unsigned int size) { void *out; @@ -4262,14 +4698,16 @@ static void duplicate_array_for_unwrapping(struct conversion_context *ctx, void *ptr = out; } -VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_count, const VkSubmitInfo2 *submits_orig, - VkFence fence, bool khr) +static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo2 *submits_orig, VkFence fence, bool khr) { + struct wine_queue *queue = wine_queue_from_handle(queue_handle); struct conversion_context ctx; VkSubmitInfo2 *submits; unsigned int i, j; VkResult ret; + TRACE("(%p, %u, %p, %s)\n", queue_handle, submit_count, submits_orig, wine_dbgstr_longlong(fence)); + init_conversion_context(&ctx); MEMDUP(&ctx, submits, submits_orig, submit_count); for (i = 0; i < submit_count; ++i) @@ -4277,16 +4715,14 @@ VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_coun duplicate_array_for_unwrapping(&ctx, (void **)&submits[i].pWaitSemaphoreInfos, submits[i].waitSemaphoreInfoCount * sizeof(*submits[i].pWaitSemaphoreInfos)); for (j = 0; j < submits[i].waitSemaphoreInfoCount; ++j) - if (submits[i].pWaitSemaphoreInfos[j].semaphore) - ((VkSemaphoreSubmitInfo *)submits[i].pWaitSemaphoreInfos)[j].semaphore - = wine_semaphore_host_handle(wine_semaphore_from_handle(submits[i].pWaitSemaphoreInfos[j].semaphore)); + unwrap_semaphore(queue->device, &((VkSemaphoreSubmitInfo *)submits[i].pWaitSemaphoreInfos)[j].semaphore, + &((VkSemaphoreSubmitInfo *)submits[i].pWaitSemaphoreInfos)[j].value, FALSE); duplicate_array_for_unwrapping(&ctx, (void **)&submits[i].pSignalSemaphoreInfos, submits[i].signalSemaphoreInfoCount * sizeof(*submits[i].pSignalSemaphoreInfos)); for (j = 0; j < submits[i].signalSemaphoreInfoCount; ++j) - if (submits[i].pSignalSemaphoreInfos[j].semaphore) - ((VkSemaphoreSubmitInfo *)submits[i].pSignalSemaphoreInfos)[j].semaphore - = wine_semaphore_host_handle(wine_semaphore_from_handle(submits[i].pSignalSemaphoreInfos[j].semaphore)); + unwrap_semaphore(queue->device, &((VkSemaphoreSubmitInfo *)submits[i].pSignalSemaphoreInfos)[j].semaphore, + &((VkSemaphoreSubmitInfo *)submits[i].pSignalSemaphoreInfos)[j].value, TRUE); if (submits[i].pCommandBufferInfos && submits[i].commandBufferInfoCount) { @@ -4297,6 +4733,7 @@ VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_coun = wine_cmd_buffer_from_handle(submits[i].pCommandBufferInfos[j].commandBuffer)->command_buffer; } } + if (khr) ret = queue->device->funcs.p_vkQueueSubmit2KHR(queue->queue, submit_count, submits, fence); else @@ -4305,39 +4742,6 @@ VkResult vk_queue_submit_2_unwrap(struct wine_queue *queue, uint32_t submit_coun return ret; } -static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence, bool khr) -{ - struct wine_queue *queue = wine_queue_from_handle(queue_handle); - unsigned int i, k; - - TRACE("(%p, %u, %p, %s)\n", queue_handle, submit_count, submits, wine_dbgstr_longlong(fence)); - - for (i = 0; i < submit_count; i++) - { - for (k = 0; k < submits[i].waitSemaphoreInfoCount; k++) - { - if (wine_semaphore_from_handle(submits[i].pWaitSemaphoreInfos[k].semaphore)->handle_type == - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - FIXME("Queue submissions with waits on D3D12-Fence compatible timeline semaphores not supported.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - - for (k = 0; k < submits[i].signalSemaphoreInfoCount; k++) - { - if (wine_semaphore_from_handle(submits[i].pSignalSemaphoreInfos[k].semaphore)->handle_type == - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - { - FIXME("Queue submissions signalling D3D12-Fence compatible timeline semaphores not supported.\n"); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - } - } - - return vk_queue_submit_2_unwrap(queue, submit_count, submits, fence, khr); -} - VkResult wine_vkQueueSubmit2(VkQueue queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence) { return vk_queue_submit_2(queue, submit_count, submits, fence, false); @@ -4350,6 +4754,7 @@ VkResult wine_vkQueueSubmit2KHR(VkQueue queue, uint32_t submit_count, const VkSu VkResult wine_vkQueuePresentKHR(VkQueue queue_handle, const VkPresentInfoKHR *present_info) { + struct wine_queue *queue = wine_queue_from_handle(queue_handle); VkPresentInfoKHR host_present_info = *present_info; struct wine_semaphore *semaphore; struct conversion_context ctx; @@ -4370,7 +4775,8 @@ VkResult wine_vkQueuePresentKHR(VkQueue queue_handle, const VkPresentInfoKHR *pr } init_conversion_context(&ctx); - host_present_info.pWaitSemaphores = unwrap_semaphore_array(present_info->pWaitSemaphores, present_info->waitSemaphoreCount, &ctx); + unwrap_semaphore_array(&host_present_info.pWaitSemaphores, NULL, present_info->waitSemaphoreCount, &ctx, + FALSE, queue->device); ret = fshack_vk_queue_present(queue_handle, &host_present_info); free_conversion_context(&ctx); return ret; @@ -4418,8 +4824,8 @@ VkResult wine_vkQueueBindSparse(VkQueue queue_handle, uint32_t bind_info_count, for (i = 0; i < bind_info_count; ++i) { batch = (VkBindSparseInfo *)&bind_info[i]; - batch->pWaitSemaphores = unwrap_semaphore_array(batch->pWaitSemaphores, batch->waitSemaphoreCount, &ctx); - batch->pSignalSemaphores = unwrap_semaphore_array(batch->pSignalSemaphores, batch->signalSemaphoreCount, &ctx); + unwrap_semaphore_array(&batch->pWaitSemaphores, NULL, batch->waitSemaphoreCount, &ctx, FALSE, queue->device); + unwrap_semaphore_array(&batch->pSignalSemaphores, NULL, batch->signalSemaphoreCount, &ctx, TRUE, queue->device); duplicate_array_for_unwrapping(&ctx, (void **)&batch->pBufferBinds, batch->bufferBindCount * sizeof(*batch->pBufferBinds)); for (j = 0; j < batch->bufferBindCount; ++j) diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index f89787eb67c..5855201ac36 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -24,6 +24,7 @@ #define VK_NO_PROTOTYPES #include +#include #include "vulkan_loader.h" #include "vulkan_thunks.h" @@ -53,6 +54,25 @@ static inline struct wine_cmd_buffer *wine_cmd_buffer_from_handle(VkCommandBuffe return (struct wine_cmd_buffer *)(uintptr_t)handle->base.unix_handle; } +struct wine_semaphore; + +struct local_timeline_semaphore +{ + VkSemaphore sem; + uint64_t value; +}; + +struct pending_d3d12_fence_op +{ + /* Vulkan native local semaphore. */ + struct local_timeline_semaphore local_sem; + + /* Operation values. */ + struct wine_vk_mapping mapping; + struct list entry; + uint64_t virtual_value; +}; + struct wine_device { struct vulkan_device_funcs funcs; @@ -67,6 +87,16 @@ struct wine_device VkQueueFamilyProperties *queue_props; struct wine_vk_mapping mapping; + + pthread_t signaller_thread; + pthread_mutex_t signaller_mutex; + bool stop; + struct list free_fence_ops_list; + struct list sem_poll_list; + struct local_timeline_semaphore sem_poll_update; + pthread_cond_t sem_poll_updated_cond; + uint64_t sem_poll_update_value; /* set to sem_poll_update.value by signaller thread once update is processed. */ + unsigned int allocated_fence_ops_count; }; static inline struct wine_device *wine_device_from_handle(VkDevice handle) @@ -326,7 +356,6 @@ static inline void free_conversion_context(struct conversion_context *pool) struct wine_semaphore { VkSemaphore semaphore; - VkSemaphore fence_timeline_semaphore; VkExternalSemaphoreHandleTypeFlagBits export_types; @@ -334,12 +363,18 @@ struct wine_semaphore /* mutable members */ VkExternalSemaphoreHandleTypeFlagBits handle_type; + struct list poll_entry; + struct list pending_waits; + struct list pending_signals; HANDLE handle; struct { + /* Shared mem access mutex. The non-shared parts access is guarded with device global signaller_mutex. */ pthread_mutex_t mutex; - uint64_t virtual_value; + uint64_t virtual_value, physical_value; } *d3d12_fence_shm; + /* The Vulkan shared semaphore is only waited or signaled in signaller_worker(). */ + VkSemaphore fence_timeline_semaphore; }; static inline struct wine_semaphore *wine_semaphore_from_handle(VkSemaphore handle) From ee026cb595e864a0688f0daa808f4b0f8f22efa8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 3 Aug 2023 13:38:37 -0600 Subject: [PATCH 702/758] winex11.drv: Override GL vendor for Paradox Launcher. CW-Bug-Id: #22492 --- dlls/winex11.drv/opengl.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index b5fd4788291..cedc17894d8 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -37,6 +37,8 @@ #ifdef HAVE_SYS_UN_H #include #endif +#include +#include #include "x11drv.h" #include "xcomposite.h" @@ -3348,6 +3350,52 @@ static void wglFlush(void) static const GLubyte *wglGetString(GLenum name) { + static int override_vendor = -1; + if (override_vendor == -1) + { + int fd; + char buffer[4096], *env; + int sz; + + override_vendor = 0; + if ((env = getenv("WINE_GL_HIDE_NVIDIA"))) + { + override_vendor = env[0] != '0'; + } + else + { + fd = open("/proc/self/cmdline", O_RDONLY); + if (fd != -1) + { + if ((sz = read(fd, buffer, sizeof(buffer) - 1)) > 0) + { + buffer[sz] = 0; + if (strstr(buffer, "\\Paradox Launcher.exe")) + { + FIXME("HACK: overriding GL vendor and renderer.\n"); + override_vendor = 1; + } + } + close(fd); + } + } + } + if (override_vendor) + { + const char *s; + if (name == GL_RENDERER) + { + s = pglGetString(name); + if (s && strstr(s, "NVIDIA")) return (const GLubyte *)"AMD Radeon Graphics"; + return s; + } + else if (name == GL_VENDOR) + { + s = pglGetString(name); + if (s && strstr(s, "NVIDIA")) return (const GLubyte *)"AMD"; + return s; + } + } if (name == GL_EXTENSIONS && glExtensions) return (const GLubyte *)glExtensions; return pglGetString(name); } From 16441f65f0710f95bfcf99b9e9f1fb9867f887c9 Mon Sep 17 00:00:00 2001 From: "Olivier F. R. Dierick" Date: Wed, 6 Oct 2021 19:59:25 +0200 Subject: [PATCH 703/758] dxdiag: Ignore option /64bit on the commandline. The builtin dxdiag doesn't know about option /64bit and display an error dialog. Some games use that option internally on startup and the dialog is annoying. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49964 (cherry picked from commit 60ef0f86772559187c04098e4c3d8b4678062a1c) --- programs/dxdiag/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/programs/dxdiag/main.c b/programs/dxdiag/main.c index 3f41a27e2f2..47909dcf2c6 100644 --- a/programs/dxdiag/main.c +++ b/programs/dxdiag/main.c @@ -165,6 +165,12 @@ static BOOL process_command_line(const WCHAR *cmdline, struct command_line_info break; + case '6': + if (wcsnicmp(cmdline, L"64bit", 5)) + return FALSE; + cmdline += 5; + break; + case 'd': case 'D': if (wcsnicmp(cmdline, L"dontskip", 8)) From 7acc7af85c78155030bac9e152182e8fef8af2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Jul 2023 11:35:19 +0200 Subject: [PATCH 704/758] imm32: Hide the composition window if the string is empty. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55258 (cherry picked from commit 428120441bd39f9311d75aa6ebad97d342837f45) --- dlls/imm32/ime.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 0c6ed9c49e1..3217b6c6562 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -98,6 +98,8 @@ static void input_context_set_comp_str( INPUTCONTEXT *ctx, const WCHAR *str, UIN UINT size; BYTE *dst; + TRACE( "ctx %p, str %s\n", ctx, debugstr_wn( str, len ) ); + size = sizeof(*compstr); size += len * sizeof(WCHAR); /* GCS_COMPSTR */ size += len; /* GCS_COMPSTRATTR */ @@ -310,20 +312,17 @@ static void ime_ui_paint( HIMC himc, HWND hwnd ) static void ime_ui_update_window( INPUTCONTEXT *ctx, HWND hwnd ) { - COMPOSITIONSTRING *string; - - if (ctx->hCompStr) string = ImmLockIMCC( ctx->hCompStr ); - else string = NULL; + WCHAR *str; + UINT len; - if (!string || string->dwCompStrLen == 0) + if (!(str = input_context_get_comp_str( ctx, FALSE, &len )) || !*str) ShowWindow( hwnd, SW_HIDE ); else { ShowWindow( hwnd, SW_SHOWNOACTIVATE ); RedrawWindow( hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE ); } - - if (string) ImmUnlockIMCC( ctx->hCompStr ); + free( str ); ctx->hWnd = GetFocus(); } From 4f986ac09230febebd91865886e0b38aa2220770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 8 Aug 2023 17:55:31 +0200 Subject: [PATCH 705/758] imm32: Return success from WM_IME_CONTROL. CW-Bug-Id: #22569 --- dlls/imm32/ime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 3217b6c6562..38a2a070117 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -434,7 +434,7 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP case WM_IME_CONTROL: FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc, debugstr_wm_ime(msg), debugstr_imc(wparam), lparam ); - return 1; + return 0; } return DefWindowProcW( hwnd, msg, wparam, lparam ); From 057597c34e2166db4c733aad887f5874afdab707 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 9 Aug 2023 16:05:55 -0600 Subject: [PATCH 706/758] nsiproxy.sys: HACK: Add an option to fake eth adapter presence. CW-Bug-Id: #22580 --- dlls/nsiproxy.sys/ndis.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dlls/nsiproxy.sys/ndis.c b/dlls/nsiproxy.sys/ndis.c index 3d3791d3560..9c4fe80e865 100644 --- a/dlls/nsiproxy.sys/ndis.c +++ b/dlls/nsiproxy.sys/ndis.c @@ -106,6 +106,8 @@ struct if_entry static struct list if_list = LIST_INIT( if_list ); static pthread_mutex_t if_list_lock = PTHREAD_MUTEX_INITIALIZER; +static BOOL have_ethernet_iface; + static struct if_entry *find_entry_from_index( UINT index ) { struct if_entry *entry; @@ -275,7 +277,22 @@ static WCHAR *strdupAtoW( const char *str ) return ret; } -static struct if_entry *add_entry( UINT index, char *name ) +static int fake_ethernet_adapter(void) +{ + static int cached = -1; + + if (cached == -1) + { + const char *s; + if ((s = getenv( "WINE_FAKE_ETH_PRESENCE" ))) + cached = atoi( s ); + else + cached = (s = getenv( "SteamGameId" )) && !strcmp( s, "1293830" ); + } + return cached; +} + +static struct if_entry *add_entry( UINT index, const char *name ) { struct if_entry *entry; int name_len = strlen( name ); @@ -304,6 +321,10 @@ static struct if_entry *add_entry( UINT index, char *name ) memcpy( entry->if_guid.Data4 + 2, "NetDev", 6 ); list_add_tail( &if_list, &entry->entry ); + + if (entry->if_luid.Info.IfType == MIB_IF_TYPE_ETHERNET) + have_ethernet_iface = TRUE; + return entry; } @@ -311,6 +332,7 @@ static unsigned int update_if_table( void ) { struct if_nameindex *indices = if_nameindex(), *entry; unsigned int append_count = 0; + struct if_entry *if_entry; for (entry = indices; entry->if_index; entry++) { @@ -319,6 +341,14 @@ static unsigned int update_if_table( void ) } if_freenameindex( indices ); + + if (!have_ethernet_iface && fake_ethernet_adapter() && (if_entry = add_entry( 0xdeadbeef, "eth0faked" ))) + { + if_entry->if_type = if_entry->if_luid.Info.IfType = MIB_IF_TYPE_ETHERNET; + have_ethernet_iface = TRUE; + ++append_count; + } + return append_count; } From 4abc6b91050887192499d99229efb6b53dcad572 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 7 Aug 2023 20:52:16 -0600 Subject: [PATCH 707/758] fixup! winevulkan: Support waiting for and signalling d3d12 shared fences. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 52 ++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index d5254629ec9..e2176ef6d23 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -139,11 +139,28 @@ static void signal_timeline_sem(struct wine_device *device, VkSemaphore sem, uin info.semaphore = sem; ++*value; info.value = *value; - res = device->funcs.p_vkSignalSemaphore(device->device, &info); + if (device->phys_dev->api_version < VK_API_VERSION_1_2 || device->phys_dev->instance->api_version < VK_API_VERSION_1_2) + res = device->funcs.p_vkSignalSemaphoreKHR(device->device, &info); + else + res = device->funcs.p_vkSignalSemaphore(device->device, &info); if (res != VK_SUCCESS) fprintf(stderr, "err:winevulkan:signal_timeline_sem vkSignalSemaphore failed, res=%d.\n", res); } +static VkResult wait_semaphores(struct wine_device *device, const VkSemaphoreWaitInfo *wait_info, uint64_t timeout) +{ + if (device->phys_dev->api_version < VK_API_VERSION_1_2 || device->phys_dev->instance->api_version < VK_API_VERSION_1_2) + return device->funcs.p_vkWaitSemaphoresKHR(device->device, wait_info, timeout); + return device->funcs.p_vkWaitSemaphores(device->device, wait_info, timeout); +} + +static VkResult get_semaphore_value(struct wine_device *device, VkSemaphore sem, uint64_t *value) +{ + if (device->phys_dev->api_version < VK_API_VERSION_1_2 || device->phys_dev->instance->api_version < VK_API_VERSION_1_2) + return device->funcs.p_vkGetSemaphoreCounterValueKHR(device->device, sem, value); + return device->funcs.p_vkGetSemaphoreCounterValue(device->device, sem, value); +} + static VkBool32 debug_utils_callback_conversion(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, @@ -3846,7 +3863,7 @@ static int semaphore_process(struct wine_device *device, struct wine_semaphore * if (op->virtual_value <= sem->d3d12_fence_shm->virtual_value) goto signal_op_complete; - res = device->funcs.p_vkGetSemaphoreCounterValue(device->device, op->local_sem.sem, &value); + res = get_semaphore_value(device, op->local_sem.sem, &value); if (res != VK_SUCCESS) { fprintf(stderr, "err:winevulkan:semaphore_process vkGetSemaphoreCounterValue failed, res=%d.\n", res); @@ -3946,7 +3963,7 @@ void *signaller_worker(void *arg) device->sem_poll_update_value = device->sem_poll_update.value; pthread_cond_signal(&device->sem_poll_updated_cond); pthread_mutex_unlock(&device->signaller_mutex); - while ((res = device->funcs.p_vkWaitSemaphores(device->device, &wait_info, 3000000000ull)) == VK_TIMEOUT) + while ((res = wait_semaphores(device, &wait_info, 3000000000ull)) == VK_TIMEOUT) { if (wait_info.semaphoreCount > 1) fprintf(stderr, "err:winevulkan:signaller_worker wait timed out with non-empty poll list.\n"); @@ -4079,9 +4096,10 @@ static void add_sem_wait_op(struct wine_device *device, struct wine_semaphore *s } static void add_sem_signal_op(struct wine_device *device, struct wine_semaphore *semaphore, uint64_t virtual_value, - VkSemaphore *phys_semaphore, uint64_t *phys_signal_value) + VkSemaphore *phys_semaphore, uint64_t *phys_signal_value, BOOL signal_immediate) { struct pending_d3d12_fence_op *op; + uint64_t value; pthread_mutex_lock(&device->signaller_mutex); if ((op = get_free_fence_op(device))) @@ -4091,9 +4109,20 @@ static void add_sem_signal_op(struct wine_device *device, struct wine_semaphore *phys_signal_value = op->local_sem.value + 1; list_add_tail(&semaphore->pending_signals, &op->entry); WINE_VK_ADD_NON_DISPATCHABLE_MAPPING(device->phys_dev->instance, semaphore, op->local_sem.sem, op); - signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); - TRACE("added signal op, semaphore %p, %s, temp sem %s, %s.\n", semaphore, wine_dbgstr_longlong(virtual_value), - wine_dbgstr_longlong(op->local_sem.sem), wine_dbgstr_longlong(op->local_sem.value)); + if (signal_immediate) + { + value = op->local_sem.value; + signal_timeline_sem(device, op->local_sem.sem, &value); + update_sem_poll_wait_processed(device); + TRACE("signal op %p, semaphore %p, %s, temp sem %s, %s.\n", op, semaphore, wine_dbgstr_longlong(virtual_value), + wine_dbgstr_longlong(op->local_sem.sem), wine_dbgstr_longlong(op->local_sem.value)); + } + else + { + signal_timeline_sem(device, device->sem_poll_update.sem, &device->sem_poll_update.value); + TRACE("added signal op, semaphore %p, %s, temp sem %s, %s.\n", semaphore, wine_dbgstr_longlong(virtual_value), + wine_dbgstr_longlong(op->local_sem.sem), wine_dbgstr_longlong(op->local_sem.value)); + } } else { @@ -4495,7 +4524,10 @@ static NTSTATUS wine_vk_signal_semaphore(VkDevice device_handle, const VkSemapho TRACE("(%p, %p)\n", device, signal_info); if (semaphore->handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT) - add_sem_signal_op(device, semaphore, signal_info->value, &dup_signal_info.semaphore, &dup_signal_info.value); + { + add_sem_signal_op(device, semaphore, signal_info->value, &dup_signal_info.semaphore, &dup_signal_info.value, TRUE); + return VK_SUCCESS; + } else dup_signal_info.semaphore = wine_semaphore_host_handle(semaphore); @@ -4528,7 +4560,7 @@ static void unwrap_semaphore(struct wine_device *device, VkSemaphore *sem_handle return; } if (signal) - add_sem_signal_op(device, sem, *value, sem_handle, value); + add_sem_signal_op(device, sem, *value, sem_handle, value, FALSE); else add_sem_wait_op(device, sem, *value, sem_handle, value); } @@ -4574,7 +4606,7 @@ static VkResult unwrap_semaphore_array(const VkSemaphore **sems, const uint64_t memcpy((void *)*values_out, values, count * sizeof(*values)); } if (signal) - add_sem_signal_op(device, sem, values[i], &out[i], (uint64_t *)&(*values_out)[i]); + add_sem_signal_op(device, sem, values[i], &out[i], (uint64_t *)&(*values_out)[i], FALSE); else add_sem_wait_op(device, sem, values[i], &out[i], (uint64_t *)&(*values_out)[i]); } From 303f3acb1cc549b362196a8591e9d654d2c208f1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 7 Aug 2023 17:49:30 -0600 Subject: [PATCH 708/758] winevulkan: Support resetting shared fence value. CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 54 ++++++++++++++++++++++++++------ dlls/winevulkan/vulkan_private.h | 10 ++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e2176ef6d23..ddfd31478c6 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -137,8 +137,8 @@ static void signal_timeline_sem(struct wine_device *device, VkSemaphore sem, uin info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO; info.semaphore = sem; - ++*value; - info.value = *value; + info.value = *value + 1; + __atomic_store_n(value, info.value, __ATOMIC_RELEASE); if (device->phys_dev->api_version < VK_API_VERSION_1_2 || device->phys_dev->instance->api_version < VK_API_VERSION_1_2) res = device->funcs.p_vkSignalSemaphoreKHR(device->device, &info); else @@ -3855,14 +3855,13 @@ static int semaphore_process(struct wine_device *device, struct wine_semaphore * int virtual_value_updated = 0; uint64_t value, virtual_value; VkResult res; + uint32_t i; /* Check local pending signal ops completion, update shared semaphore. */ d3d12_semaphore_lock( sem ); + virtual_value = sem->d3d12_fence_shm->virtual_value; LIST_FOR_EACH_ENTRY_SAFE(op, op2, &sem->pending_signals, struct pending_d3d12_fence_op, entry) { - if (op->virtual_value <= sem->d3d12_fence_shm->virtual_value) - goto signal_op_complete; - res = get_semaphore_value(device, op->local_sem.sem, &value); if (res != VK_SUCCESS) { @@ -3879,28 +3878,64 @@ static int semaphore_process(struct wine_device *device, struct wine_semaphore * continue; } + virtual_value = max( sem->d3d12_fence_shm->virtual_value, op->virtual_value ); sem->d3d12_fence_shm->virtual_value = op->virtual_value; virtual_value_updated = 1; signal_op_complete: - ++op->local_sem.value; + op->local_sem.value = value; release_fence_op(device, op); } + if (sem->d3d12_fence_shm->virtual_value < virtual_value) + { + uint32_t idx = sem->d3d12_fence_shm->reset_backlog_count; + + if (debug_level >= 3) + fprintf(stderr, "warn:winevulkan:semaphore_process resetting semaphore %p virtual value.\n", sem); + if (idx == ARRAY_SIZE(sem->d3d12_fence_shm->reset_backlog)) + { + sem->d3d12_fence_shm->last_dropped_reset_physical = sem->d3d12_fence_shm->reset_backlog[0].physical_at_reset; + --idx; + memmove(&sem->d3d12_fence_shm->reset_backlog[0], &sem->d3d12_fence_shm->reset_backlog[1], + sizeof(*sem->d3d12_fence_shm->reset_backlog) * sem->d3d12_fence_shm->reset_backlog_count); + } + else + { + ++sem->d3d12_fence_shm->reset_backlog_count; + } + sem->d3d12_fence_shm->last_reset_physical = sem->d3d12_fence_shm->physical_value + 1; + sem->d3d12_fence_shm->reset_backlog[idx].physical_at_reset = sem->d3d12_fence_shm->last_reset_physical; + sem->d3d12_fence_shm->reset_backlog[idx].virtual_before_reset = virtual_value; + } if (virtual_value_updated) signal_timeline_sem(device, sem->fence_timeline_semaphore, &sem->d3d12_fence_shm->physical_value); global_sem_wait_value = sem->d3d12_fence_shm->physical_value + 1; - virtual_value = sem->d3d12_fence_shm->virtual_value; - d3d12_semaphore_unlock(sem); /* Complete satisfied local waits. */ LIST_FOR_EACH_ENTRY_SAFE(op, op2, &sem->pending_waits, struct pending_d3d12_fence_op, entry) { if (op->virtual_value > virtual_value) - continue; + { + if (op->shared_physical_value > sem->d3d12_fence_shm->last_reset_physical) + continue; + for (i = 0; i < sem->d3d12_fence_shm->reset_backlog_count; ++i) + { + if (sem->d3d12_fence_shm->reset_backlog[i].physical_at_reset >= op->shared_physical_value + && sem->d3d12_fence_shm->reset_backlog[i].virtual_before_reset >= op->virtual_value) + break; + } + if (i == sem->d3d12_fence_shm->reset_backlog_count) + { + if (sem->d3d12_fence_shm->last_dropped_reset_physical < op->shared_physical_value) + continue; + fprintf(stderr, "err:winevulkan:semaphore_process wait needs reset backlog beyond cut off.\n"); + } + } signal_timeline_sem(device, op->local_sem.sem, &op->local_sem.value); release_fence_op(device, op); } + d3d12_semaphore_unlock(sem); /* Only poll shared semaphore if there are waits pending. */ if (list_empty(&sem->pending_waits)) @@ -4079,6 +4114,7 @@ static void add_sem_wait_op(struct wine_device *device, struct wine_semaphore *s if ((op = get_free_fence_op(device))) { op->virtual_value = virtual_value; + op->shared_physical_value = __atomic_load_n(&semaphore->d3d12_fence_shm->physical_value, __ATOMIC_ACQUIRE) + 1; *phys_semaphore = op->local_sem.sem; *phys_wait_value = op->local_sem.value + 1; list_add_tail(&semaphore->pending_waits, &op->entry); diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index 5855201ac36..beae80cd929 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -71,6 +71,7 @@ struct pending_d3d12_fence_op struct wine_vk_mapping mapping; struct list entry; uint64_t virtual_value; + uint64_t shared_physical_value; }; struct wine_device @@ -372,6 +373,15 @@ struct wine_semaphore /* Shared mem access mutex. The non-shared parts access is guarded with device global signaller_mutex. */ pthread_mutex_t mutex; uint64_t virtual_value, physical_value; + uint64_t last_reset_physical; + uint64_t last_dropped_reset_physical; + struct + { + uint64_t physical_at_reset; + uint64_t virtual_before_reset; + } + reset_backlog[16]; + uint32_t reset_backlog_count; } *d3d12_fence_shm; /* The Vulkan shared semaphore is only waited or signaled in signaller_worker(). */ VkSemaphore fence_timeline_semaphore; From 7ca3c2b2da7eccd03c59a80625dba1aa62fcbd5a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 4 Aug 2023 13:14:43 -0600 Subject: [PATCH 709/758] winevulkan: Also remap Win32 functions in vk_is_available_instance_function(). CW-Bug-Id: #22526 --- dlls/winevulkan/vulkan.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index ddfd31478c6..99b30d8a25d 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3606,12 +3606,23 @@ VkResult fshack_vk_queue_present(VkQueue queue_handle, const VkPresentInfoKHR *p return res; } +static void substitute_function_name(const char **name) +{ + if (!strcmp(*name, "vkGetMemoryWin32HandleKHR") || !strcmp(*name, "vkGetMemoryWin32HandlePropertiesKHR")) + *name = "vkGetMemoryFdKHR"; + else if (!strcmp(*name, "vkGetSemaphoreWin32HandleKHR")) + *name = "vkGetSemaphoreFdKHR"; + else if (!strcmp(*name, "vkImportSemaphoreWin32HandleKHR")) + *name = "vkImportSemaphoreFdKHR"; +} + #ifdef _WIN64 NTSTATUS vk_is_available_instance_function(void *arg) { struct is_available_instance_function_params *params = arg; struct wine_instance *instance = wine_instance_from_handle(params->instance); + substitute_function_name(¶ms->name); return !!vk_funcs->p_vkGetInstanceProcAddr(instance->instance, params->name); } @@ -3619,12 +3630,7 @@ NTSTATUS vk_is_available_device_function(void *arg) { struct is_available_device_function_params *params = arg; struct wine_device *device = wine_device_from_handle(params->device); - if (!strcmp(params->name, "vkGetMemoryWin32HandleKHR") || !strcmp(params->name, "vkGetMemoryWin32HandlePropertiesKHR")) - params->name = "vkGetMemoryFdKHR"; - else if (!strcmp(params->name, "vkGetSemaphoreWin32HandleKHR")) - params->name = "vkGetSemaphoreFdKHR"; - else if (!strcmp(params->name, "vkImportSemaphoreWin32HandleKHR")) - params->name = "vkImportSemaphoreFdKHR"; + substitute_function_name(¶ms->name); return !!vk_funcs->p_vkGetDeviceProcAddr(device->device, params->name); } @@ -3638,7 +3644,9 @@ NTSTATUS vk_is_available_instance_function32(void *arg) UINT32 name; } *params = arg; struct wine_instance *instance = wine_instance_from_handle(UlongToPtr(params->instance)); - return !!vk_funcs->p_vkGetInstanceProcAddr(instance->instance, UlongToPtr(params->name)); + const char *name = UlongToPtr(params->name); + substitute_function_name(&name); + return !!vk_funcs->p_vkGetInstanceProcAddr(instance->instance, name); } NTSTATUS vk_is_available_device_function32(void *arg) @@ -3649,14 +3657,9 @@ NTSTATUS vk_is_available_device_function32(void *arg) UINT32 name; } *params = arg; struct wine_device *device = wine_device_from_handle(UlongToPtr(params->device)); - char *name = UlongToPtr(params->name); - if (!strcmp(name, "vkGetMemoryWin32HandleKHR") || !strcmp(name, "vkGetMemoryWin32HandlePropertiesKHR")) - return !!vk_funcs->p_vkGetDeviceProcAddr(device->device, "vkGetMemoryFdKHR"); - if (!strcmp(name, "vkGetSemaphoreWin32HandleKHR")) - return !!vk_funcs->p_vkGetDeviceProcAddr(device->device, "vkGetSemaphoreFdKHR"); - if (!strcmp(name, "vkImportSemaphoreWin32HandleKHR")) - return !!vk_funcs->p_vkGetDeviceProcAddr(device->device, "vkImportSemaphoreFdKHR"); - return !!vk_funcs->p_vkGetDeviceProcAddr(device->device, UlongToPtr(params->name)); + const char *name = UlongToPtr(params->name); + substitute_function_name(&name); + return !!vk_funcs->p_vkGetDeviceProcAddr(device->device, name); } VkDevice WINAPI __wine_get_native_VkDevice(VkDevice handle) From 9fe7370533b5bc1662385bb753aa416ecd4353bb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 4 Aug 2023 19:38:36 -0600 Subject: [PATCH 710/758] fixup! winevulkan: Implement support for KMT handles and named objects. CW-Bug-Id: #22372 --- dlls/sharedgpures.sys/shared_resource.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/sharedgpures.sys/shared_resource.c b/dlls/sharedgpures.sys/shared_resource.c index d8eaea26348..c6ab013ea0d 100644 --- a/dlls/sharedgpures.sys/shared_resource.c +++ b/dlls/sharedgpures.sys/shared_resource.c @@ -168,13 +168,12 @@ static NTSTATUS shared_resource_open(struct shared_resource **res, void *buff, S /* name lookup */ for (i = 0; i < resource_pool_size; i++) { - if (!wcscmp(resource_pool[i].name, &input->name[0])) + if (resource_pool[i].name && !wcscmp(resource_pool[i].name, input->name)) { *res = &resource_pool[i]; break; } } - if (i == resource_pool_size) return STATUS_OBJECT_NAME_NOT_FOUND; } From 3c2c79e3e49c0051071ee9d97cb5f3239b7225e6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 4 Aug 2023 14:30:26 -0600 Subject: [PATCH 711/758] winevulkan: Expose VK_KHR_win32_keyed_mutex extension. CW-Bug-Id: #22372 --- dlls/winevulkan/make_vulkan | 5 ++--- dlls/winevulkan/vulkan.c | 44 +++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 1332875e3ef..98b861b8eb3 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -102,7 +102,6 @@ UNSUPPORTED_EXTENSIONS = [ # Relates to external_semaphore and needs type conversions in bitflags. "VK_KHR_shared_presentable_image", # Needs WSI work. "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml - "VK_KHR_win32_keyed_mutex", "VK_NV_external_memory_rdma", # Needs shared resources work. # Extensions for other platforms @@ -311,8 +310,8 @@ STRUCT_CHAIN_CONVERSIONS = { "VkPhysicalDeviceImageFormatInfo2": [], "VkPhysicalDeviceExternalSemaphoreInfo": [], "VkSemaphoreCreateInfo": ["VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR"], - "VkSubmitInfo": ["VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR"], - "VkSubmitInfo2": [], + "VkSubmitInfo": ["VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR", "VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR"], + "VkSubmitInfo2": ["VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR"], "VkBindSparseInfo" : [], } diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 99b30d8a25d..25e477df42a 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -281,7 +281,7 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance uint32_t num_host_properties, num_properties = 0; VkExtensionProperties *host_properties = NULL; VkPhysicalDeviceProperties physdev_properties; - BOOL have_external_memory_host = FALSE; + BOOL have_external_memory_host = FALSE, have_external_memory_fd = FALSE, have_external_semaphore_fd = FALSE; VkResult res; unsigned int i, j; @@ -335,6 +335,7 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance snprintf(host_properties[i].extensionName, sizeof(host_properties[i].extensionName), VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME); host_properties[i].specVersion = VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION; + have_external_memory_fd = TRUE; } if (!strcmp(host_properties[i].extensionName, "VK_KHR_external_semaphore_fd")) { @@ -343,6 +344,7 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance snprintf(host_properties[i].extensionName, sizeof(host_properties[i].extensionName), VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME); host_properties[i].specVersion = VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION; + have_external_semaphore_fd = TRUE; } if (wine_vk_device_extension_supported(host_properties[i].extensionName)) @@ -358,7 +360,8 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance have_external_memory_host = TRUE; } - TRACE("Host supported extensions %u, Wine supported extensions %u\n", num_host_properties, num_properties); + if (have_external_memory_fd && have_external_semaphore_fd) + ++num_properties; /* VK_KHR_win32_keyed_mutex */ if (!(object->extensions = calloc(num_properties, sizeof(*object->extensions)))) { @@ -374,7 +377,15 @@ static struct wine_phys_dev *wine_vk_physical_device_alloc(struct wine_instance j++; } } + if (have_external_memory_fd && have_external_semaphore_fd) + { + strcpy(object->extensions[j].extensionName, VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME); + object->extensions[j].specVersion = VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION; + TRACE("Enabling extension '%s' for physical device %p\n", object->extensions[j].extensionName, object); + ++j; + } object->extension_count = num_properties; + TRACE("Host supported extensions %u, Wine supported extensions %u\n", num_host_properties, num_properties); if (use_external_memory() && have_external_memory_host) { @@ -523,7 +534,7 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de struct conversion_context *ctx, const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) { static const char *wine_xr_extension_name = "VK_WINE_openxr_device_extensions"; - unsigned int i, append_xr = 0, replace_win32 = 0, append_timeline = 1; + unsigned int i, append_xr = 0, have_ext_mem32 = 0, have_ext_sem32 = 0, have_keyed_mutex = 0, append_timeline = 1; VkBaseOutStructure *header; char **xr_extensions_list; @@ -543,8 +554,12 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de if (!strcmp(extension_name, wine_xr_extension_name)) append_xr = 1; - else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32") || !strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_semaphore_win32")) - replace_win32 = 1; + else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32")) + have_ext_mem32 = 1; + else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_semaphore_win32")) + have_ext_sem32 = 1; + else if (!strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_win32_keyed_mutex")) + have_keyed_mutex = 1; else if (!strcmp(extension_name, "VK_KHR_timeline_semaphore")) append_timeline = 0; } @@ -566,7 +581,7 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de if (append_xr) xr_extensions_list = parse_xr_extensions(&append_xr); - if (phys_dev->external_memory_align || append_xr || replace_win32 || append_timeline) + if (phys_dev->external_memory_align || append_xr || have_ext_mem32 || have_ext_sem32 || have_keyed_mutex || append_timeline) { const char **new_extensions; unsigned int o = 0, count; @@ -578,19 +593,30 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de count += append_xr - 1; if (append_timeline) ++count; + if (have_keyed_mutex) + count += !have_ext_mem32 + !have_ext_sem32; new_extensions = conversion_context_alloc(ctx, count * sizeof(*dst->ppEnabledExtensionNames)); for (i = 0; i < dst->enabledExtensionCount; ++i) { if (append_xr && !strcmp(src->ppEnabledExtensionNames[i], wine_xr_extension_name)) continue; - if (replace_win32 && !strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32")) + if (have_ext_mem32 && !strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_memory_win32")) new_extensions[o++] = "VK_KHR_external_memory_fd"; - else if (replace_win32 && !strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_semaphore_win32")) + else if (have_ext_sem32 && !strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_external_semaphore_win32")) new_extensions[o++] = "VK_KHR_external_semaphore_fd"; + else if (have_keyed_mutex && !strcmp(src->ppEnabledExtensionNames[i], "VK_KHR_win32_keyed_mutex")) + continue; else new_extensions[o++] = src->ppEnabledExtensionNames[i]; } + if (have_keyed_mutex) + { + if (!have_ext_mem32) + new_extensions[o++] = "VK_KHR_external_memory_fd"; + if (!have_ext_sem32) + new_extensions[o++] = "VK_KHR_external_semaphore_fd"; + } if (phys_dev->external_memory_align) { new_extensions[o++] = "VK_KHR_external_memory"; @@ -603,7 +629,7 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de } if (append_timeline) new_extensions[o++] = "VK_KHR_timeline_semaphore"; - dst->enabledExtensionCount = count; + dst->enabledExtensionCount = o; dst->ppEnabledExtensionNames = new_extensions; } From 88104058297e488d1148768c859c3a7ef8887a77 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 4 Aug 2023 17:52:14 -0600 Subject: [PATCH 712/758] winevulkan: Implement vkGetMemoryWin32HandlePropertiesKHR(). CW-Bug-Id: #22372 --- dlls/winevulkan/vulkan.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 25e477df42a..b58f9d04c6f 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3762,13 +3762,25 @@ VkResult wine_vkGetMemoryWin32HandleKHR(VkDevice device, const VkMemoryGetWin32H } } -VkResult wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties) +VkResult wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device_handle, VkExternalMemoryHandleTypeFlagBits type, HANDLE handle, VkMemoryWin32HandlePropertiesKHR *properties) { + struct wine_device *device = wine_device_from_handle(device_handle); + unsigned int i; + TRACE("%p %u %p %p\n", device, type, handle, properties); - /* VUID-vkGetMemoryWin32HandlePropertiesKHR-handleType-00666 - handleType must not be one of the handle types defined as opaque */ - return VK_ERROR_INVALID_EXTERNAL_HANDLE; + if (!(type & wine_vk_handle_over_fd_types)) + { + FIXME("type %#x.\n", type); + return VK_ERROR_INVALID_EXTERNAL_HANDLE; + } + + properties->memoryTypeBits = 0; + for (i = 0; i < device->phys_dev->memory_properties.memoryTypeCount; ++i) + if (device->phys_dev->memory_properties.memoryTypes[i].propertyFlags == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + properties->memoryTypeBits |= 1u << i; + + return VK_SUCCESS; } #define IOCTL_SHARED_GPU_RESOURCE_SET_OBJECT CTL_CODE(FILE_DEVICE_VIDEO, 6, METHOD_BUFFERED, FILE_WRITE_ACCESS) From 0cf87d845254b82f8373c8c4eecbf99cc0f0fb7e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 4 Aug 2023 19:13:05 -0600 Subject: [PATCH 713/758] winevulkan: Use resource allocated size when importing shared textures. CW-Bug-Id: #22372 --- dlls/sharedgpures.sys/shared_resource.c | 28 +++++++++++++ dlls/winevulkan/vulkan.c | 54 +++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/dlls/sharedgpures.sys/shared_resource.c b/dlls/sharedgpures.sys/shared_resource.c index c6ab013ea0d..ad836662af0 100644 --- a/dlls/sharedgpures.sys/shared_resource.c +++ b/dlls/sharedgpures.sys/shared_resource.c @@ -27,6 +27,7 @@ struct shared_resource SIZE_T metadata_size; void **object_pool; unsigned int object_pool_count; + UINT64 resource_size; }; static struct shared_resource *resource_pool; @@ -72,6 +73,7 @@ static void *reference_client_handle(obj_handle_t handle) struct shared_resource_create { + UINT64 resource_size; obj_handle_t unix_handle; WCHAR name[1]; }; @@ -125,6 +127,7 @@ static NTSTATUS shared_resource_create(struct shared_resource **res, void *buff, (*res)->ref_count = 1; (*res)->unix_resource = unix_resource; (*res)->name = name; + (*res)->resource_size = input->resource_size; iosb->Information = 0; return STATUS_SUCCESS; @@ -138,6 +141,11 @@ struct shared_resource_open WCHAR name[1]; }; +struct shared_resource_info +{ + UINT64 resource_size; +}; + static unsigned int kmt_to_index(obj_handle_t kmt) { if (!(kmt & 0x40000000) || (kmt - 2) % 4) @@ -340,6 +348,20 @@ static NTSTATUS shared_resource_get_object(struct shared_resource *res, void *bu return STATUS_SUCCESS; } +#define IOCTL_SHARED_GPU_RESOURCE_GET_INFO CTL_CODE(FILE_DEVICE_VIDEO, 7, METHOD_BUFFERED, FILE_READ_ACCESS) +static NTSTATUS shared_resource_get_info(struct shared_resource *res, void *buff, SIZE_T outsize, IO_STATUS_BLOCK *iosb) +{ + struct shared_resource_info *info = buff; + + if (sizeof(*info) > outsize) + return STATUS_BUFFER_TOO_SMALL; + + info->resource_size = res->resource_size; + iosb->Information = sizeof(*info); + return STATUS_SUCCESS; +} + + static NTSTATUS WINAPI dispatch_create(DEVICE_OBJECT *device, IRP *irp) { irp->IoStatus.u.Status = STATUS_SUCCESS; @@ -452,6 +474,12 @@ static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp) stack->Parameters.DeviceIoControl.OutputBufferLength, &irp->IoStatus); break; + case IOCTL_SHARED_GPU_RESOURCE_GET_INFO: + status = shared_resource_get_info( res, + irp->AssociatedIrp.SystemBuffer, + stack->Parameters.DeviceIoControl.OutputBufferLength, + &irp->IoStatus ); + break; default: FIXME( "ioctl %#lx not supported\n", stack->Parameters.DeviceIoControl.IoControlCode ); status = STATUS_NOT_SUPPORTED; diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index b58f9d04c6f..bfa9d1f6471 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2564,11 +2564,12 @@ void wine_vkDestroySurfaceKHR(VkInstance handle, VkSurfaceKHR surface, struct shared_resource_create { + UINT64 resource_size; obj_handle_t unix_handle; WCHAR name[1]; }; -static HANDLE create_gpu_resource(int fd, LPCWSTR name) +static HANDLE create_gpu_resource(int fd, LPCWSTR name, UINT64 resource_size) { static const WCHAR shared_gpu_resourceW[] = {'\\','?','?','\\','S','h','a','r','e','d','G','p','u','R','e','s','o','u','r','c','e',0}; HANDLE unix_resource = INVALID_HANDLE_VALUE; @@ -2604,6 +2605,7 @@ static HANDLE create_gpu_resource(int fd, LPCWSTR name) in_size = sizeof(*inbuff) + (name ? lstrlenW(name) * sizeof(WCHAR) : 0); inbuff = calloc(1, in_size); inbuff->unix_handle = wine_server_obj_handle(unix_resource); + inbuff->resource_size = resource_size; if (name) lstrcpyW(&inbuff->name[0], name); @@ -2631,6 +2633,11 @@ struct shared_resource_open WCHAR name[1]; }; +struct shared_resource_info +{ + UINT64 resource_size; +}; + static HANDLE open_shared_resource(HANDLE kmt_handle, LPCWSTR name) { static const WCHAR shared_gpu_resourceW[] = {'\\','?','?','\\','S','h','a','r','e','d','G','p','u','R','e','s','o','u','r','c','e',0}; @@ -2678,6 +2685,21 @@ static HANDLE open_shared_resource(HANDLE kmt_handle, LPCWSTR name) return shared_resource; } +#define IOCTL_SHARED_GPU_RESOURCE_GET_INFO CTL_CODE(FILE_DEVICE_VIDEO, 7, METHOD_BUFFERED, FILE_READ_ACCESS) + +static BOOL shared_resource_get_info(HANDLE handle, struct shared_resource_info *info) +{ + IO_STATUS_BLOCK iosb; + unsigned int status; + + status = NtDeviceIoControlFile(handle, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_GET_INFO, + NULL, 0, info, sizeof(*info)); + if (status) + ERR("Failed to get shared resource info, status %#x.\n", status); + + return !status; +} + #define IOCTL_SHARED_GPU_RESOURCE_GET_UNIX_RESOURCE CTL_CODE(FILE_DEVICE_VIDEO, 3, METHOD_BUFFERED, FILE_READ_ACCESS) static int get_shared_resource_fd(HANDLE shared_resource) @@ -2756,11 +2778,15 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo /* Vulkan consumes imported FDs, but not imported HANDLEs */ if (handle_import_info) { + struct shared_resource_info res_info; + fd_import_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; fd_import_info.pNext = info.pNext; fd_import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; info.pNext = &fd_import_info; + TRACE("import handle type %#x.\n", handle_import_info->handleType); + switch (handle_import_info->handleType) { case VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT: @@ -2796,6 +2822,26 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo result = VK_ERROR_INVALID_EXTERNAL_HANDLE; goto done; } + + /* From VkMemoryAllocateInfo spec: "if the parameters define an import operation and the external handle type is + * VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT, + * or VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT, allocationSize is ignored.". Although test suggests + * that it is also true for opaque Win32 handles. */ + if (shared_resource_get_info(memory->handle, &res_info)) + { + if (res_info.resource_size) + { + TRACE("Shared resource size %llu.\n", (long long)res_info.resource_size); + if (info.allocationSize && info.allocationSize != res_info.resource_size) + FIXME("Shared resource allocationSize %llu, resource_size %llu.\n", + (long long)info.allocationSize, (long long)res_info.resource_size); + info.allocationSize = res_info.resource_size; + } + else + { + ERR("Zero shared resource size.\n"); + } + } } else if (device->phys_dev->external_memory_align && (mem_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && !find_next_struct(alloc_info->pNext, VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT)) @@ -2876,7 +2922,7 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo if (device->funcs.p_vkGetMemoryFdKHR(device->device, &get_fd_info, &fd) == VK_SUCCESS) { - memory->handle = create_gpu_resource(fd, handle_export_info ? handle_export_info->name : NULL); + memory->handle = create_gpu_resource(fd, handle_export_info ? handle_export_info->name : NULL, alloc_info->allocationSize); memory->access = handle_export_info ? handle_export_info->dwAccess : GENERIC_ALL; if (handle_export_info && handle_export_info->pAttributes) memory->inherit = handle_export_info->pAttributes->bInheritHandle; @@ -4261,7 +4307,7 @@ VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateI if ((res = device->funcs.p_vkGetSemaphoreFdKHR(device->device, &fd_info, &fd)) == VK_SUCCESS) { - object->handle = create_gpu_resource(fd, export_handle_info ? export_handle_info->name : NULL); + object->handle = create_gpu_resource(fd, export_handle_info ? export_handle_info->name : NULL, 0); close(fd); } @@ -4300,7 +4346,7 @@ VkResult wine_vkCreateSemaphore(VkDevice device_handle, const VkSemaphoreCreateI if ((res = device->funcs.p_vkGetSemaphoreFdKHR(device->device, &fd_info, &fd)) == VK_SUCCESS) { - object->handle = create_gpu_resource(fd, export_handle_info ? export_handle_info->name : NULL); + object->handle = create_gpu_resource(fd, export_handle_info ? export_handle_info->name : NULL, 0); close(fd); } From c51439dce4cc70ea36c93fa31f81a030b718cfe9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 10 Aug 2023 20:09:33 -0600 Subject: [PATCH 714/758] winevulkan: Share keyed mutex data. CW-Bug-Id: #22372 --- dlls/winevulkan/vulkan.c | 195 ++++++++++++++++++++++++++++++- dlls/winevulkan/vulkan_private.h | 14 +++ 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index bfa9d1f6471..12f1a665b7f 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -531,7 +531,8 @@ static char **parse_xr_extensions(unsigned int *len) } static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_dev, - struct conversion_context *ctx, const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst) + struct conversion_context *ctx, const VkDeviceCreateInfo *src, VkDeviceCreateInfo *dst, + struct wine_device *device) { static const char *wine_xr_extension_name = "VK_WINE_openxr_device_extensions"; unsigned int i, append_xr = 0, have_ext_mem32 = 0, have_ext_sem32 = 0, have_keyed_mutex = 0, append_timeline = 1; @@ -616,6 +617,7 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de new_extensions[o++] = "VK_KHR_external_memory_fd"; if (!have_ext_sem32) new_extensions[o++] = "VK_KHR_external_semaphore_fd"; + device->keyed_mutexes_enabled = TRUE; } if (phys_dev->external_memory_align) { @@ -993,7 +995,7 @@ VkResult wine_vkCreateDevice(VkPhysicalDevice phys_dev_handle, const VkDeviceCre } init_conversion_context(&ctx); - res = wine_vk_device_convert_create_info(phys_dev, &ctx, create_info, &create_info_host); + res = wine_vk_device_convert_create_info(phys_dev, &ctx, create_info, &create_info_host, object); if (res == VK_SUCCESS) { VkPhysicalDeviceFeatures features = {0}; @@ -2732,6 +2734,190 @@ static HANDLE get_shared_resource_kmt_handle(HANDLE shared_resource) return wine_server_ptr_handle(kmt_handle); } +static bool set_shared_resource_object(HANDLE shared_resource, unsigned int index, HANDLE handle); +static HANDLE get_shared_resource_object(HANDLE shared_resource, unsigned int index); + +static void destroy_keyed_mutex(struct wine_device *device, struct wine_device_memory *memory) +{ + if (memory->keyed_mutex_shm) + { + NtUnmapViewOfSection(GetCurrentProcess(), memory->keyed_mutex_shm); + memory->keyed_mutex_shm = NULL; + } + if (memory->keyed_mutex_sem) + { + device->funcs.p_vkDestroySemaphore(device->device, memory->keyed_mutex_sem, NULL); + memory->keyed_mutex_sem = VK_NULL_HANDLE; + } +} + +static void create_keyed_mutex(struct wine_device *device, struct wine_device_memory *memory) +{ + VkExportSemaphoreCreateInfo timeline_export_info; + VkSemaphoreTypeCreateInfo type_info; + VkSemaphoreCreateInfo create_info; + VkSemaphoreGetFdInfoKHR fd_info; + pthread_mutexattr_t mutex_attr; + OBJECT_ATTRIBUTES attr; + HANDLE section_handle; + LARGE_INTEGER li; + HANDLE handle; + SIZE_T size; + VkResult vr; + int fd; + + InitializeObjectAttributes(&attr, NULL, 0, NULL, NULL); + size = li.QuadPart = sizeof(*memory->keyed_mutex_shm); + if (NtCreateSection(§ion_handle, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr, &li, PAGE_READWRITE, SEC_COMMIT, NULL)) + { + ERR("NtCreateSection failed.\n"); + return; + } + + if (!set_shared_resource_object(memory->handle, 0, section_handle)) + { + NtClose(section_handle); + ERR("set_shared_resource_object failed.\n"); + return; + } + + if (NtMapViewOfSection(section_handle, GetCurrentProcess(), (void**) &memory->keyed_mutex_shm, 0, 0, NULL, &size, ViewShare, 0, PAGE_READWRITE)) + { + NtClose(section_handle); + ERR("NtMapViewOfSection failed.\n"); + return; + } + + NtClose(section_handle); + + timeline_export_info.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + timeline_export_info.pNext = NULL; + timeline_export_info.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; + + type_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + type_info.pNext = &timeline_export_info; + type_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + type_info.initialValue = 0; + + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + create_info.pNext = &type_info; + create_info.flags = 0; + + if ((vr = device->funcs.p_vkCreateSemaphore(device->device, &create_info, NULL, &memory->keyed_mutex_sem)) != VK_SUCCESS) + { + ERR("Failed to create semaphore, vr %d.\n", vr); + goto error; + } + fd_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + fd_info.pNext = NULL; + fd_info.semaphore = memory->keyed_mutex_sem; + fd_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; + + if ((vr = device->funcs.p_vkGetSemaphoreFdKHR(device->device, &fd_info, &fd)) != VK_SUCCESS) + { + ERR("Failed to export semaphore fd, vr %d.\n", vr); + goto error; + } + if (wine_server_fd_to_handle(fd, GENERIC_ALL, 0, &handle) != STATUS_SUCCESS) + { + ERR("wine_server_fd_to_handle failed.\n"); + close(fd); + goto error; + } + close(fd); + if (!set_shared_resource_object(memory->handle, 1, handle)) + { + ERR("set_shared_resource_object failed.\n"); + NtClose(handle); + goto error; + } + NtClose(handle); + + pthread_mutexattr_init(&mutex_attr); + pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); + if (pthread_mutex_init(&memory->keyed_mutex_shm->mutex, &mutex_attr)) + memory->keyed_mutex_shm->instance_id_counter = 1; + memory->keyed_mutex_instance_id = ++memory->keyed_mutex_shm->instance_id_counter; + TRACE("memory %p, created keyed mutex.\n", memory); + return; + +error: + destroy_keyed_mutex(device, memory); +} + +static void import_keyed_mutex(struct wine_device *device, struct wine_device_memory *memory) +{ + VkSemaphoreTypeCreateInfo type_info; + VkImportSemaphoreFdInfoKHR fd_info; + VkSemaphoreCreateInfo create_info; + HANDLE section_handle, sem_handle; + SIZE_T size; + + VkResult vr; + + if (!(section_handle = get_shared_resource_object(memory->handle, 0))) + { + TRACE("No section handle.\n"); + return; + } + if (!(sem_handle = get_shared_resource_object(memory->handle, 1))) + { + ERR("No smeaphore handle.\n"); + NtClose(section_handle); + return; + } + + size = sizeof(*memory->keyed_mutex_shm); + if (NtMapViewOfSection(section_handle, GetCurrentProcess(), (void**) &memory->keyed_mutex_shm, 0, 0, NULL, &size, ViewShare, 0, PAGE_READWRITE)) + { + ERR("NtMapViewOfSection failed.\n"); + goto error; + } + + type_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + type_info.pNext = NULL; + type_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + type_info.initialValue = 0; + + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + create_info.pNext = &type_info; + create_info.flags = 0; + + if ((vr = device->funcs.p_vkCreateSemaphore(device->device, &create_info, NULL, &memory->keyed_mutex_sem)) != VK_SUCCESS) + { + ERR("Failed to create semaphore, vr %d.\n", vr); + goto error; + } + + fd_info.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + fd_info.pNext = NULL; + fd_info.semaphore = memory->keyed_mutex_sem; + fd_info.flags = 0; + fd_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; + + if (wine_server_handle_to_fd(sem_handle, FILE_READ_DATA, &fd_info.fd, NULL)) + { + ERR("wine_server_handle_to_fd failed.\n"); + goto error; + } + + vr = device->funcs.p_vkImportSemaphoreFdKHR(device->device, &fd_info); + close(fd_info.fd); + if (vr != VK_SUCCESS) + { + ERR("vkImportSemaphoreFdKHR failed, vr %d.\n", vr); + goto error; + } + + memory->keyed_mutex_instance_id = InterlockedIncrement64((LONGLONG *)&memory->keyed_mutex_shm->instance_id_counter); + TRACE("memory %p, imported keyed mutex.\n", memory); + return; +error: + NtClose(section_handle); + NtClose(sem_handle); + destroy_keyed_mutex(device, memory); +} + VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *alloc_info, const VkAllocationCallbacks *allocator, VkDeviceMemory *ret, void *win_pAllocateInfo) @@ -2842,6 +3028,8 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo ERR("Zero shared resource size.\n"); } } + if (device->keyed_mutexes_enabled) + import_keyed_mutex(device, memory); } else if (device->phys_dev->external_memory_align && (mem_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && !find_next_struct(alloc_info->pNext, VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT)) @@ -2929,6 +3117,8 @@ VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *allo else memory->inherit = FALSE; close(fd); + if (device->keyed_mutexes_enabled) + create_keyed_mutex(device, memory); } if (memory->handle == INVALID_HANDLE_VALUE) @@ -2963,6 +3153,7 @@ void wine_vkFreeMemory(VkDevice handle, VkDeviceMemory memory_handle, const VkAl return; memory = wine_device_memory_from_handle(memory_handle); + destroy_keyed_mutex(device, memory); device->funcs.p_vkFreeMemory(device->device, memory->memory, NULL); if (memory->mapping) diff --git a/dlls/winevulkan/vulkan_private.h b/dlls/winevulkan/vulkan_private.h index beae80cd929..c9548e944d2 100644 --- a/dlls/winevulkan/vulkan_private.h +++ b/dlls/winevulkan/vulkan_private.h @@ -98,6 +98,7 @@ struct wine_device pthread_cond_t sem_poll_updated_cond; uint64_t sem_poll_update_value; /* set to sem_poll_update.value by signaller thread once update is processed. */ unsigned int allocated_fence_ops_count; + BOOL keyed_mutexes_enabled; }; static inline struct wine_device *wine_device_from_handle(VkDevice handle) @@ -248,6 +249,16 @@ static inline struct wine_cmd_pool *wine_cmd_pool_from_handle(VkCommandPool hand return (struct wine_cmd_pool *)(uintptr_t)client_ptr->unix_handle; } +struct keyed_mutex_shm +{ + pthread_mutex_t mutex; + uint64_t instance_id_counter; + uint64_t acquired_to_instance; + uint64_t key; + uint64_t timeline_value; + uint64_t timeline_queued_release; +}; + struct wine_device_memory { VkDeviceMemory memory; @@ -256,6 +267,9 @@ struct wine_device_memory DWORD access; HANDLE handle; void *mapping; + struct keyed_mutex_shm *keyed_mutex_shm; + VkSemaphore keyed_mutex_sem; + uint64_t keyed_mutex_instance_id; }; static inline VkDeviceMemory wine_device_memory_to_handle(struct wine_device_memory *device_memory) From e9afcae272f9e725515321147434ba03ba9c5b1a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 4 Aug 2023 20:46:30 -0600 Subject: [PATCH 715/758] winevulkan: Support keyed mutex waits and signals in submits. CW-Bug-Id: #22372 --- dlls/winevulkan/make_vulkan | 4 +- dlls/winevulkan/vulkan.c | 284 ++++++++++++++++++++++++++++++++++-- 2 files changed, 271 insertions(+), 17 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 98b861b8eb3..f10bcf12a59 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -220,7 +220,7 @@ FUNCTION_OVERRIDES = { "vkWaitSemaphores" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueBindSparse" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, "vkQueueSubmit" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, - "vkQueueSubmit2" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, + "vkQueueSubmit2" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, # VK_KHR_surface "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE}, @@ -295,7 +295,7 @@ FUNCTION_OVERRIDES = { "vkWaitSemaphoresKHR" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, # VK_KHR_synchronization2 - "vkQueueSubmit2KHR" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, + "vkQueueSubmit2KHR" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, } STRUCT_CHAIN_CONVERSIONS = { diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 12f1a665b7f..e962eea4524 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2918,6 +2918,111 @@ static void import_keyed_mutex(struct wine_device *device, struct wine_device_me destroy_keyed_mutex(device, memory); } +static VkResult acquire_keyed_mutex(struct wine_device *device, struct wine_device_memory *memory, uint64_t key, + uint32_t timeout_ms) +{ + ULONG end_wait, curr_tick, remaining_wait; + VkSemaphoreWaitInfo wait_info = { 0 }; + uint64_t timeline; + VkResult vr; + + if (!memory->keyed_mutex_shm) + return VK_ERROR_UNKNOWN; + + wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; + wait_info.semaphoreCount = 1; + wait_info.pSemaphores = &memory->keyed_mutex_sem; + wait_info.pValues = &timeline; + + end_wait = NtGetTickCount() + timeout_ms; + + while (1) + { + pthread_mutex_lock(&memory->keyed_mutex_shm->mutex); + + if (memory->keyed_mutex_shm->acquired_to_instance) + { + if ((vr = get_semaphore_value(device, memory->keyed_mutex_sem, &timeline)) != VK_SUCCESS) + { + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + return VK_ERROR_UNKNOWN; + } + assert(timeline == memory->keyed_mutex_shm->timeline_value + || timeline == memory->keyed_mutex_shm->timeline_value + 1); + if (timeline == memory->keyed_mutex_shm->timeline_value + 1) + { + /* released from queue. */ + assert(memory->keyed_mutex_shm->timeline_queued_release == timeline); + memory->keyed_mutex_shm->timeline_queued_release = 0; + ++memory->keyed_mutex_shm->timeline_value; + memory->keyed_mutex_shm->acquired_to_instance = 0; + } + } + + if (memory->keyed_mutex_shm->acquired_to_instance == memory->keyed_mutex_instance_id + && !memory->keyed_mutex_shm->timeline_queued_release) + { + /* Already acquired to this device. */ + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + return VK_ERROR_UNKNOWN; + } + if (!memory->keyed_mutex_shm->acquired_to_instance && memory->keyed_mutex_shm->key == key) + { + /* Can acquire. */ + memory->keyed_mutex_shm->acquired_to_instance = memory->keyed_mutex_instance_id; + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + return VK_SUCCESS; + } + curr_tick = NtGetTickCount(); + if (!timeout_ms || curr_tick >= end_wait) + { + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + return VK_TIMEOUT; + } + remaining_wait = timeout_ms == INFINITE ? INFINITE : end_wait - curr_tick; + timeline = memory->keyed_mutex_shm->timeline_value + 1; + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + + vr = wait_semaphores(device, &wait_info, remaining_wait * 1000000ull); + if (vr != VK_SUCCESS && vr != VK_TIMEOUT) + { + ERR("vkWaitSemaphores failed, vr %d.\n", vr); + return VK_ERROR_UNKNOWN; + } + } +} + +static VkResult release_keyed_mutex(struct wine_device *device, struct wine_device_memory *memory, uint64_t key, + uint64_t *timeline_value) +{ + if (!memory->keyed_mutex_shm) + return VK_ERROR_UNKNOWN; + + pthread_mutex_lock(&memory->keyed_mutex_shm->mutex); + if (memory->keyed_mutex_shm->acquired_to_instance != memory->keyed_mutex_instance_id + || memory->keyed_mutex_shm->timeline_queued_release) + { + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + return VK_ERROR_UNKNOWN; + } + memory->keyed_mutex_shm->key = key; + if (timeline_value) + { + /* Return timeline value to signal from queue. */ + *timeline_value = memory->keyed_mutex_shm->timeline_value + 1; + memory->keyed_mutex_shm->timeline_queued_release = *timeline_value; + } + else + { + /* Release immediately. */ + memory->keyed_mutex_shm->acquired_to_instance = 0; + signal_timeline_sem(device, memory->keyed_mutex_sem, &memory->keyed_mutex_shm->timeline_value); + } + pthread_mutex_unlock(&memory->keyed_mutex_shm->mutex); + + return VK_SUCCESS; +} + VkResult wine_vkAllocateMemory(VkDevice handle, const VkMemoryAllocateInfo *alloc_info, const VkAllocationCallbacks *allocator, VkDeviceMemory *ret, void *win_pAllocateInfo) @@ -4969,17 +5074,127 @@ struct struct_chain_def unsigned int size; }; +static VkResult process_keyed_mutexes(struct conversion_context *ctx, struct wine_device *device, + uint32_t submit_count, const void *submits_win, size_t submit_size, uint32_t **signal_counts, + VkSemaphoreSubmitInfo ***signal_infos) +{ + VkWin32KeyedMutexAcquireReleaseInfoKHR *keyed_mutex_info; + struct wine_device_memory *memory; + VkResult ret = VK_ERROR_UNKNOWN; + uint32_t i, j, signal_count = 0; + void *ptr; + + for (i = 0; i < submit_count; ++i) + { + ptr = (char *)submits_win + i * submit_size; + if (!(keyed_mutex_info = wine_vk_find_struct(ptr, WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR))) + continue; + for (j = 0; j < keyed_mutex_info->acquireCount; ++j) + { + memory = wine_device_memory_from_handle(keyed_mutex_info->pAcquireSyncs[j]); + if ((ret = acquire_keyed_mutex(device, memory, keyed_mutex_info->pAcquireKeys[j], + keyed_mutex_info->pAcquireTimeouts[j])) == VK_SUCCESS) + continue; + while (j) + { + --j; + memory = wine_device_memory_from_handle(keyed_mutex_info->pAcquireSyncs[j]); + release_keyed_mutex(device, memory, keyed_mutex_info->pAcquireKeys[j], NULL); + } + goto error; + } + /* Pre-check release error conditions. */ + for (j = 0; j < keyed_mutex_info->releaseCount; ++j) + { + memory = wine_device_memory_from_handle(keyed_mutex_info->pReleaseSyncs[j]); + if (!memory->keyed_mutex_shm) + goto error; + if (memory->keyed_mutex_shm->acquired_to_instance != memory->keyed_mutex_instance_id) + goto error; + } + signal_count += keyed_mutex_info->releaseCount; + } + + if (!signal_count) + { + *signal_counts = NULL; + return VK_SUCCESS; + } + *signal_counts = conversion_context_alloc(ctx, sizeof(**signal_counts) * submit_count); + *signal_infos = conversion_context_alloc(ctx, sizeof(**signal_infos) * submit_count); + for (i = 0; i < submit_count; ++i) + { + ptr = (char *)submits_win + i * submit_size; + if (!(keyed_mutex_info = wine_vk_find_struct(ptr, WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR))) + { + (*signal_counts)[i] = 0; + continue; + } + (*signal_counts)[i] = keyed_mutex_info->releaseCount; + (*signal_infos)[i] = conversion_context_alloc(ctx, sizeof(***signal_infos) * keyed_mutex_info->releaseCount); + for (j = 0; j < keyed_mutex_info->releaseCount; ++j) + { + memory = wine_device_memory_from_handle(keyed_mutex_info->pReleaseSyncs[j]); + (*signal_infos)[i][j].sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; + (*signal_infos)[i][j].pNext = NULL; + (*signal_infos)[i][j].semaphore = memory->keyed_mutex_sem; + (*signal_infos)[i][j].stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + (*signal_infos)[i][j].deviceIndex = 0; + ret = release_keyed_mutex(device, memory, keyed_mutex_info->pReleaseKeys[j], &(*signal_infos)[i][j].value); + if (ret != VK_SUCCESS) + { + /* This should only be possible if a racing submit queued release before us, currently not handled. */ + ERR("release_keyed_mutex failed, ret %d.\n", ret); + (*signal_infos)[i][j].value = 0; + } + } + } + + return VK_SUCCESS; + +error: + while (i) + { + --i; + ptr = (char *)submits_win + i * submit_size; + if (!(keyed_mutex_info = wine_vk_find_struct(ptr, WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR))) + continue; + for (j = 0; j < keyed_mutex_info->acquireCount; ++j) + { + memory = wine_device_memory_from_handle(keyed_mutex_info->pAcquireSyncs[j]); + release_keyed_mutex(device, memory, keyed_mutex_info->pAcquireKeys[j], NULL); + } + } + return ret; +} + +static void duplicate_array_for_unwrapping_copy_size(struct conversion_context *ctx, void **ptr, unsigned int size, + unsigned int copy_size) +{ + void *out; + + if (!size) + return; + + out = conversion_context_alloc(ctx, size); + if (*ptr) + memcpy(out, *ptr, copy_size); + *ptr = out; +} + VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo *submits_orig, VkFence fence, void *submits_win_ptr) { struct wine_queue *queue = wine_queue_from_handle(queue_handle); struct wine_device *device = queue->device; - VkTimelineSemaphoreSubmitInfo *timeline_submit_info; + VkTimelineSemaphoreSubmitInfo *timeline_submit_info, ts_info_copy; const VkSubmitInfo *submits_win = submits_win_ptr; VkD3D12FenceSubmitInfoKHR *d3d12_submit_info; const uint64_t **values; struct conversion_context ctx; VkSubmitInfo *submits; + VkSemaphoreSubmitInfo **km_infos; + uint32_t *km_counts; unsigned int i, j; VkResult ret; @@ -4987,6 +5202,9 @@ VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const V init_conversion_context(&ctx); MEMDUP(&ctx, submits, submits_orig, submit_count); + if ((ret = process_keyed_mutexes(&ctx, device, submit_count, submits_win_ptr, sizeof(*submits), &km_counts, &km_infos))) + return ret; + for (i = 0; i < submit_count; ++i) { timeline_submit_info = wine_vk_find_struct(&submits[i], TIMELINE_SEMAPHORE_SUBMIT_INFO); @@ -5016,6 +5234,36 @@ VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const V else values = NULL; unwrap_semaphore_array(&submits[i].pSignalSemaphores, values, submits[i].signalSemaphoreCount, &ctx, TRUE, device); + if (km_counts && km_counts[i]) + { + if (timeline_submit_info) + { + ts_info_copy = *timeline_submit_info; + timeline_submit_info = &ts_info_copy; + duplicate_array_for_unwrapping_copy_size(&ctx, (void **)&timeline_submit_info->pSignalSemaphoreValues, + (timeline_submit_info->signalSemaphoreValueCount + km_counts[i]) * sizeof(*timeline_submit_info->pSignalSemaphoreValues), + timeline_submit_info->signalSemaphoreValueCount * sizeof(*timeline_submit_info->pSignalSemaphoreValues)); + } + else + { + timeline_submit_info = &ts_info_copy; + timeline_submit_info->sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; + timeline_submit_info->pNext = submits[i].pNext; + timeline_submit_info->waitSemaphoreValueCount = 0; + timeline_submit_info->signalSemaphoreValueCount = 0; + timeline_submit_info->pSignalSemaphoreValues = conversion_context_alloc(&ctx, km_counts[i] * sizeof(*timeline_submit_info->pSignalSemaphoreValues)); + submits[i].pNext = timeline_submit_info; + } + duplicate_array_for_unwrapping_copy_size(&ctx, (void **)&submits[i].pSignalSemaphores, + (submits[i].signalSemaphoreCount + km_counts[i]) * sizeof(*submits[i].pSignalSemaphores), + submits[i].signalSemaphoreCount * sizeof(*submits[i].pSignalSemaphores)); + for (j = 0; j < km_counts[i]; ++j) + { + ((uint64_t *)timeline_submit_info->pSignalSemaphoreValues)[j + timeline_submit_info->signalSemaphoreValueCount++] + = km_infos[i][j].value; + ((VkSemaphore *)submits[i].pSignalSemaphores)[j + submits[i].signalSemaphoreCount++] = km_infos[i][j].semaphore; + } + } if (submits[i].pCommandBuffers && submits[i].commandBufferCount) { @@ -5034,20 +5282,17 @@ VkResult wine_vkQueueSubmit(VkQueue queue_handle, uint32_t submit_count, const V static void duplicate_array_for_unwrapping(struct conversion_context *ctx, void **ptr, unsigned int size) { - void *out; - - if (!*ptr || !size) - return; - - out = conversion_context_alloc(ctx, size); - memcpy(out, *ptr, size); - *ptr = out; + duplicate_array_for_unwrapping_copy_size(ctx, ptr, size, size); } -static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo2 *submits_orig, VkFence fence, bool khr) +static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, const VkSubmitInfo2 *submits_orig, + VkFence fence, bool khr, void *submits_win_ptr) { struct wine_queue *queue = wine_queue_from_handle(queue_handle); + struct wine_device *device = queue->device; struct conversion_context ctx; + VkSemaphoreSubmitInfo **km_infos; + uint32_t *km_counts, count; VkSubmitInfo2 *submits; unsigned int i, j; VkResult ret; @@ -5056,6 +5301,8 @@ static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, c init_conversion_context(&ctx); MEMDUP(&ctx, submits, submits_orig, submit_count); + if ((ret = process_keyed_mutexes(&ctx, device, submit_count, submits_win_ptr, sizeof(*submits), &km_counts, &km_infos))) + return ret; for (i = 0; i < submit_count; ++i) { duplicate_array_for_unwrapping(&ctx, (void **)&submits[i].pWaitSemaphoreInfos, @@ -5064,11 +5311,16 @@ static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, c unwrap_semaphore(queue->device, &((VkSemaphoreSubmitInfo *)submits[i].pWaitSemaphoreInfos)[j].semaphore, &((VkSemaphoreSubmitInfo *)submits[i].pWaitSemaphoreInfos)[j].value, FALSE); - duplicate_array_for_unwrapping(&ctx, (void **)&submits[i].pSignalSemaphoreInfos, + count = submits[i].signalSemaphoreInfoCount + (km_counts ? km_counts[i] : 0); + duplicate_array_for_unwrapping_copy_size(&ctx, (void **)&submits[i].pSignalSemaphoreInfos, + count * sizeof(*submits[i].pSignalSemaphoreInfos), submits[i].signalSemaphoreInfoCount * sizeof(*submits[i].pSignalSemaphoreInfos)); for (j = 0; j < submits[i].signalSemaphoreInfoCount; ++j) unwrap_semaphore(queue->device, &((VkSemaphoreSubmitInfo *)submits[i].pSignalSemaphoreInfos)[j].semaphore, &((VkSemaphoreSubmitInfo *)submits[i].pSignalSemaphoreInfos)[j].value, TRUE); + for (; j < count; ++j) + ((VkSemaphoreSubmitInfo *)submits[i].pSignalSemaphoreInfos)[j] = km_infos[i][j - submits[i].signalSemaphoreInfoCount]; + submits[i].signalSemaphoreInfoCount = count; if (submits[i].pCommandBufferInfos && submits[i].commandBufferInfoCount) { @@ -5088,14 +5340,16 @@ static VkResult vk_queue_submit_2(VkQueue queue_handle, uint32_t submit_count, c return ret; } -VkResult wine_vkQueueSubmit2(VkQueue queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence) +VkResult wine_vkQueueSubmit2(VkQueue queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence, + void *submits_win) { - return vk_queue_submit_2(queue, submit_count, submits, fence, false); + return vk_queue_submit_2(queue, submit_count, submits, fence, false, submits_win); } -VkResult wine_vkQueueSubmit2KHR(VkQueue queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence) +VkResult wine_vkQueueSubmit2KHR(VkQueue queue, uint32_t submit_count, const VkSubmitInfo2 *submits, VkFence fence, + void *submits_win) { - return vk_queue_submit_2(queue, submit_count, submits, fence, true); + return vk_queue_submit_2(queue, submit_count, submits, fence, true, submits_win); } VkResult wine_vkQueuePresentKHR(VkQueue queue_handle, const VkPresentInfoKHR *present_info) From 607877c0f2ded6a80d48067439c987141dff5b2a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 10 Aug 2023 12:23:26 -0600 Subject: [PATCH 716/758] winevulkan: Export custom functions for acquiring and releasing keyed mutexes. CW-Bug-Id: #22372 --- dlls/winevulkan/make_vulkan | 21 +++++++++++++++------ dlls/winevulkan/vk_custom.xml | 24 ++++++++++++++++++++++++ dlls/winevulkan/vulkan.c | 12 ++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 dlls/winevulkan/vk_custom.xml diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index f10bcf12a59..ae00d208318 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -296,6 +296,10 @@ FUNCTION_OVERRIDES = { # VK_KHR_synchronization2 "vkQueueSubmit2KHR" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE, "extra_param" : "pSubmits"}, + + # Custom functions + "wine_vkAcquireKeyedMutex" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, + "wine_vkReleaseKeyedMutex" : {"dispatch": True, "driver": False, "thunk" : ThunkType.PRIVATE}, } STRUCT_CHAIN_CONVERSIONS = { @@ -3353,12 +3357,16 @@ class VkRegistry(object): # function call we want we set a member 'required' to True. tree = ET.parse(reg_filename) root = tree.getroot() + + tree_custom = ET.parse("vk_custom.xml") + root_custom = tree_custom.getroot() + self._parse_enums(root) self._parse_types(root) - self._parse_commands(root) + self._parse_commands(root, root_custom) # Pull in any required types and functions. - self._parse_features(root) + self._parse_features(root, root_custom) self._parse_extensions(root) for enum in self.enums.values(): @@ -3451,10 +3459,10 @@ class VkRegistry(object): if not handle.object_type: LOGGER.warning("No object type found for {}".format(handle.name)) - def _parse_commands(self, root): + def _parse_commands(self, root, root_custom): """ Parse command section containing the Vulkan function calls. """ funcs = {} - commands = root.findall("./commands/") + commands = root.findall("./commands/") + root_custom.findall("./commands/") # As of Vulkan 1.1, various extensions got promoted to Core. # The old commands (e.g. KHR) are available for backwards compatibility @@ -3470,6 +3478,7 @@ class VkRegistry(object): continue func = VkFunction.from_xml(command, self.types) + if func: funcs[func.name] = func @@ -3721,10 +3730,10 @@ class VkRegistry(object): # Sort in alphabetical order. self.extensions = sorted(extensions, key=lambda ext: ext["name"]) - def _parse_features(self, root): + def _parse_features(self, root, root_custom): """ Parse the feature section, which describes Core commands and types needed. """ - for feature in root.findall("./feature"): + for feature in (root.findall("./feature") + root_custom.findall("./feature")): if not api_is_vulkan(feature): continue feature_name = feature.attrib["name"] diff --git a/dlls/winevulkan/vk_custom.xml b/dlls/winevulkan/vk_custom.xml new file mode 100644 index 00000000000..a9fd68548c4 --- /dev/null +++ b/dlls/winevulkan/vk_custom.xml @@ -0,0 +1,24 @@ + + + + + VkResult wine_vkAcquireKeyedMutex + VkDevice device + VkDeviceMemory memory + uint64_t key + uint32_t timeout_ms + + + VkResult wine_vkReleaseKeyedMutex + VkDevice device + VkDeviceMemory memory + uint64_t key + + + + + + + + + diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e962eea4524..71006827b1a 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -3982,6 +3982,8 @@ static void substitute_function_name(const char **name) *name = "vkGetSemaphoreFdKHR"; else if (!strcmp(*name, "vkImportSemaphoreWin32HandleKHR")) *name = "vkImportSemaphoreFdKHR"; + else if (!strcmp(*name, "wine_vkAcquireKeyedMutex") || !strcmp(*name, "wine_vkReleaseKeyedMutex")) + *name = "vkImportSemaphoreFdKHR"; } #ifdef _WIN64 @@ -5461,3 +5463,13 @@ VkResult wine_vkQueueBindSparse(VkQueue queue_handle, uint32_t bind_info_count, free_conversion_context(&ctx); return ret; } + +VkResult wine_wine_vkAcquireKeyedMutex(VkDevice device, VkDeviceMemory memory, uint64_t key, uint32_t timeout_ms) +{ + return acquire_keyed_mutex(wine_device_from_handle(device), wine_device_memory_from_handle(memory), key, timeout_ms); +} + +VkResult wine_wine_vkReleaseKeyedMutex(VkDevice device, VkDeviceMemory memory, uint64_t key) +{ + return release_keyed_mutex(wine_device_from_handle(device), wine_device_memory_from_handle(memory), key, NULL); +} From b31cbf3a7c66b4eb4480dab71144070ed7312ba4 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 11 Aug 2023 13:12:58 -0500 Subject: [PATCH 717/758] mscoree: Update Wine Mono to 8.0.1. --- dlls/appwiz.cpl/addons.c | 4 ++-- dlls/mscoree/mscoree_private.h | 2 +- tools/gitlab/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index a53e818bbbb..3f0b8b2c03e 100644 --- a/dlls/appwiz.cpl/addons.c +++ b/dlls/appwiz.cpl/addons.c @@ -58,10 +58,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl); #define GECKO_SHA "???" #endif -#define MONO_VERSION "8.0.0" +#define MONO_VERSION "8.0.1" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "75b3f45dca1dc89857fe9e932da78710f64cc6d49ef1ab0c723a177085b4711b" +#define MONO_SHA "27240085f5b4f8b175ff0479f3d6cc4309b00adbb386c00ba1fddd30f0367976" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index 50ed656a656..ca8ba343be3 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -45,7 +45,7 @@ extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version) extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count) DECLSPEC_HIDDEN; extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func) DECLSPEC_HIDDEN; -#define WINE_MONO_VERSION "8.0.0" +#define WINE_MONO_VERSION "8.0.1" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index 04ee21eb9e7..09387a26439 100644 --- a/tools/gitlab/test.yml +++ b/tools/gitlab/test.yml @@ -7,7 +7,7 @@ variables: GIT_STRATEGY: none GECKO_VER: 2.47.3 - MONO_VER: 8.0.0 + MONO_VER: 8.0.1 cache: - key: wine-gecko-$GECKO_VER paths: From 2408b5abc78e7c46c8d4bf261c182f7ce302b69e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 11 Aug 2023 17:57:29 -0600 Subject: [PATCH 718/758] opengl32: Skip debug callback messages called from native thread. CW-Bug-Id: #22593 --- dlls/opengl32/unix_wgl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index 24bd904b068..e1ddb7d84cd 100644 --- a/dlls/opengl32/unix_wgl.c +++ b/dlls/opengl32/unix_wgl.c @@ -823,8 +823,15 @@ static void gl_debug_message_callback( GLenum source, GLenum type, GLuint id, GL }; void *ret_ptr; ULONG ret_len; - struct wgl_handle *ptr = (struct wgl_handle *)userParam; + + if (!NtCurrentTeb()) + { + fprintf( stderr, "msg:gl_debug_message_callback called from native thread, serverity %#x, message \"%.*s\".\n", + severity, length, message ); + return; + } + if (!(params.user_callback = ptr->u.context->debug_callback)) return; params.user_data = ptr->u.context->debug_user; From 2e5db678d6e8c1d7fb9d84c090acf1b594c7642e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:41:42 -0400 Subject: [PATCH 719/758] sapi: Call CoInitializeEx/CoUninitialize in async_worker. (cherry picked from commit 679e3a9d003f2bb0d11e620171a048f23ac82fc6) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/async.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index b02778ae7ba..61b99019abe 100644 --- a/dlls/sapi/async.c +++ b/dlls/sapi/async.c @@ -71,6 +71,7 @@ static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx) HANDLE handles[2] = { queue->cancel, queue->wait }; DWORD ret; + CoInitializeEx(NULL, COINIT_MULTITHREADED); SetEvent(queue->ready); for (;;) @@ -99,6 +100,7 @@ static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx) cancel: async_empty_queue(queue); + CoUninitialize(); TRACE("cancelled.\n"); SetEvent(queue->ready); } From ead9ae63123422ab06cce6ce6d2f33828541104d Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:43:13 -0400 Subject: [PATCH 720/758] sapi: Start async queue in ISpVoice::SetOutput. (cherry picked from commit cdc3eeb77638f925e2af1228f32255bcb4fd719f) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tts.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 4763ef9f324..b1c90627d5e 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -591,6 +591,9 @@ static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL all if (!allow_format_changes) FIXME("ignoring allow_format_changes = FALSE.\n"); + if (FAILED(hr = async_start_queue(&This->queue))) + return hr; + if (!unk) { /* TODO: Create the default SpAudioOut token here once SpMMAudioEnum is implemented. */ From 5e81fa425207e0f7f64bd52cdd7740231048438a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:44:04 -0400 Subject: [PATCH 721/758] sapi/tests: Add test for implicit MTA initialized by SpVoice. Based on a patch by Connor McAdams. (cherry picked from commit 738bbeb9b7d6be802119cb0d78735a1fbf023d0b) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 96 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 39f62ac551e..eb0258bbef3 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -34,6 +34,46 @@ static void _expect_ref(IUnknown *obj, ULONG ref, int line) ok_(__FILE__,line)(rc == ref, "Unexpected refcount %ld, expected %ld.\n", rc, ref); } +#define APTTYPE_UNITIALIZED APTTYPE_CURRENT +static struct +{ + APTTYPE type; + APTTYPEQUALIFIER qualifier; +} test_apt_data; + +static DWORD WINAPI test_apt_thread(void *param) +{ + HRESULT hr; + + hr = CoGetApartmentType(&test_apt_data.type, &test_apt_data.qualifier); + if (hr == CO_E_NOTINITIALIZED) + { + test_apt_data.type = APTTYPE_UNITIALIZED; + test_apt_data.qualifier = 0; + } + + return 0; +} + +static void check_apttype(void) +{ + HANDLE thread; + MSG msg; + + memset(&test_apt_data, 0xde, sizeof(test_apt_data)); + + thread = CreateThread(NULL, 0, test_apt_thread, NULL, 0, NULL); + while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + { + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + CloseHandle(thread); +} + static void test_interfaces(void) { ISpeechVoice *speech_voice, *speech_voice2; @@ -380,6 +420,7 @@ static void test_spvoice(void) static const WCHAR test_text[] = L"Hello! This is a test sentence."; ISpVoice *voice; + IUnknown *dummy; ISpMMSysAudio *audio_out; ISpObjectTokenCategory *token_cat; ISpObjectToken *token; @@ -397,23 +438,57 @@ static void test_spvoice(void) return; } + check_apttype(); + ok(test_apt_data.type == APTTYPE_UNITIALIZED, "got apt type %d.\n", test_apt_data.type); + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, &IID_ISpVoice, (void **)&voice); ok(hr == S_OK, "Failed to create SpVoice: %#lx.\n", hr); - hr = ISpVoice_SetOutput(voice, NULL, TRUE); - ok(hr == S_OK, "got %#lx.\n", hr); + check_apttype(); + ok(test_apt_data.type == APTTYPE_UNITIALIZED, "got apt type %d.\n", test_apt_data.type); + + /* SpVoice initializes a MTA in SetOutput even if an invalid output object is given. */ + hr = CoCreateInstance(&CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&dummy); + ok(hr == S_OK, "Failed to create dummy: %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, dummy, TRUE); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA || broken(test_apt_data.type == APTTYPE_UNITIALIZED) /* w8, w10v1507 */, + "got apt type %d.\n", test_apt_data.type); + if (test_apt_data.type == APTTYPE_MTA) + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + else + win_skip("apt type is not MTA.\n"); + + IUnknown_Release(dummy); hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, &IID_ISpMMSysAudio, (void **)&audio_out); ok(hr == S_OK, "Failed to create SpMMAudioOut: %#lx.\n", hr); + hr = ISpVoice_SetOutput(voice, NULL, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE); todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr); hr = ISpVoice_SetVoice(voice, NULL); todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA || broken(test_apt_data.type == APTTYPE_UNITIALIZED) /* w8, w10v1507 */, + "got apt type %d.\n", test_apt_data.type); + if (test_apt_data.type == APTTYPE_MTA) + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + else + win_skip("apt type is not MTA.\n"); + hr = ISpVoice_GetVoice(voice, &token); todo_wine ok(hr == S_OK, "got %#lx.\n", hr); @@ -517,6 +592,15 @@ static void test_spvoice(void) hr = ISpVoice_SetVoice(voice, token); ok(hr == S_OK, "got %#lx.\n", hr); + check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA || broken(test_apt_data.type == APTTYPE_UNITIALIZED) /* w8, w10v1507 */, + "got apt type %d.\n", test_apt_data.type); + if (test_apt_data.type == APTTYPE_MTA) + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + else + win_skip("apt type is not MTA.\n"); + test_engine.speak_called = FALSE; hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL); ok(hr == S_OK, "got %#lx.\n", hr); @@ -548,6 +632,11 @@ static void test_spvoice(void) ok(stream_num == 1, "got %lu.\n", stream_num); ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); + check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA, "got apt type %d.\n", test_apt_data.type); + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + start = GetTickCount(); hr = ISpVoice_WaitUntilDone(voice, INFINITE); duration = GetTickCount() - start; @@ -602,7 +691,8 @@ static void test_spvoice(void) START_TEST(tts) { CoInitialize(NULL); - test_interfaces(); + /* Run spvoice tests before interface tests so that a MTA won't be created before this test is run. */ test_spvoice(); + test_interfaces(); CoUninitialize(); } From 2ff964889d2348e4fbff8c349fd3bcfabf837753 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:51:17 -0400 Subject: [PATCH 722/758] sapi: Change ISpObjectToken::SetId FIXME to TRACE. (cherry picked from commit b9dbe482eea774122d2293a3bb3bdb0f0e645a88) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/token.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index af98db02048..f599bdb6b14 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1305,7 +1305,7 @@ static HRESULT WINAPI token_SetId( ISpObjectToken *iface, HKEY root, key; const WCHAR *subkey; - FIXME( "(%p)->(%s %s %d): semi-stub\n", This, debugstr_w( category_id ), + TRACE( "(%p)->(%s %s %d)\n", This, debugstr_w( category_id ), debugstr_w(token_id), create ); if (This->data_key) return SPERR_ALREADY_INITIALIZED; From 09253f39ed441ad300cacc4b52bea49dba192ebb Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:57:55 -0400 Subject: [PATCH 723/758] sapi/tests: Increase timeout in tts test_spvoice. (cherry picked from commit cff2e87e03ff045fbe1a3718a81574fe077ab189) CW-Bug-Id: #18723 CW-Bug-Id: #20918 CW-Bug-Id: #21259 --- dlls/sapi/tests/tts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index eb0258bbef3..6912dc08e0d 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -630,7 +630,7 @@ static void test_spvoice(void) ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); ok(stream_num == 1, "got %lu.\n", stream_num); - ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); + ok(duration > 800 && duration < 3500, "took %lu ms.\n", duration); check_apttype(); ok(test_apt_data.type == APTTYPE_MTA, "got apt type %d.\n", test_apt_data.type); @@ -658,7 +658,7 @@ static void test_spvoice(void) hr = ISpVoice_WaitUntilDone(voice, INFINITE); duration = GetTickCount() - start; ok(hr == S_OK, "got %#lx.\n", hr); - ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration); + ok(duration > 800 && duration < 3500, "took %lu ms.\n", duration); ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags); From cc31e1a6079b24f3b669b2a81fbc5a4c7dff4e9f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 14 Aug 2023 15:38:49 -0600 Subject: [PATCH 724/758] winex11.drv: Fix buffer allocation size in import_xdnd_selection(). Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55443 CW-Bug-Id: #22600 --- dlls/winex11.drv/clipboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c index 327e74d73dd..5b426f52df5 100644 --- a/dlls/winex11.drv/clipboard.c +++ b/dlls/winex11.drv/clipboard.c @@ -1308,7 +1308,7 @@ struct format_entry *import_xdnd_selection( Display *display, Window win, Atom s if (!(data = import_selection( display, win, selection, format, &size ))) continue; entry_size = (FIELD_OFFSET( struct format_entry, data[size] ) + 7) & ~7; - if (buf_size < size + entry_size) + if (buf_size < *ret_size + entry_size) { if (!(ret = realloc( ret, *ret_size + entry_size + 1024 ))) continue; buf_size = *ret_size + entry_size + 1024; /* extra space for following entries */ From e2d58d4b42102a961a0bfdcf57ce98c736ed82f9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 14 Aug 2023 16:41:03 -0600 Subject: [PATCH 725/758] amd_ags_x64, atiadlxx: Bump driver version. --- dlls/amd_ags_x64/amd_ags_x64_main.c | 4 ++-- dlls/atiadlxx/atiadlxx_main.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index facea4ff622..8e216cdde2d 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -22,8 +22,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(amd_ags); -static const char driver_version[] = "23.10.01.45-230626a-393367C-AMD-Software-Adrenalin-Edition"; -static const char radeon_version[] = "23.7.1"; +static const char driver_version[] = "23.10.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition"; +static const char radeon_version[] = "23.7.2"; enum amd_ags_version { diff --git a/dlls/atiadlxx/atiadlxx_main.c b/dlls/atiadlxx/atiadlxx_main.c index 7104bcfa723..d5faa39bcbb 100644 --- a/dlls/atiadlxx/atiadlxx_main.c +++ b/dlls/atiadlxx/atiadlxx_main.c @@ -172,15 +172,15 @@ typedef struct ADLDisplayMap } ADLDisplayMap, *LPADLDisplayMap; static const ADLVersionsInfo version = { - "23.10.01.45-230626a-393367C-AMD-Software-Adrenalin-Edition", + "23.10.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition", "", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; static const ADLVersionsInfoX2 version2 = { - "23.10.01.45-230626a-393367C-AMD-Software-Adrenalin-Edition", + "23.10.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition", "", - "23.7.1", + "23.7.2", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; From ab491716861f26b171a11f213c70b12219c9fbd0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 14 Aug 2023 19:45:10 -0600 Subject: [PATCH 726/758] fixup! amd_ags_x64, atiadlxx: Bump driver version. CW-Bug-Id: #22607 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 2 +- dlls/atiadlxx/atiadlxx_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 8e216cdde2d..6a8167d0a59 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -23,7 +23,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(amd_ags); static const char driver_version[] = "23.10.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition"; -static const char radeon_version[] = "23.7.2"; +static const char radeon_version[] = "23.8.1"; enum amd_ags_version { diff --git a/dlls/atiadlxx/atiadlxx_main.c b/dlls/atiadlxx/atiadlxx_main.c index d5faa39bcbb..8c52dbdc601 100644 --- a/dlls/atiadlxx/atiadlxx_main.c +++ b/dlls/atiadlxx/atiadlxx_main.c @@ -180,7 +180,7 @@ static const ADLVersionsInfo version = { static const ADLVersionsInfoX2 version2 = { "23.10.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition", "", - "23.7.2", + "23.8.1", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; From d2b68a5066c742f80953b8e662667ecb6af38cc5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 14 Aug 2023 20:04:14 -0600 Subject: [PATCH 727/758] wine.inf: Enable builtin amd_ags_x64, atiadlxx for Ratchet & Clank: Rift Apart. CW-Bug-Id: #22607 --- loader/wine.inf.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index d497ae555b2..6273413d92e 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2851,6 +2851,7 @@ HKCU,Software\Wine\AppDefaults\ShadowOfWar.exe\DllOverrides,"amd_ags_x64",,"disa HKCU,Software\Wine\AppDefaults\u4.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" HKCU,Software\Wine\AppDefaults\tll.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" HKCU,Software\Wine\AppDefaults\SOPFFO.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" +HKCU,Software\Wine\AppDefaults\RiftApart.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" ;;App-specific overrides for atiadlxx.dll. HKCU,Software\Wine\AppDefaults\s2_sp64_ship.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\s2_mp64_ship.exe\DllOverrides,"atiadlxx",,"builtin" @@ -2871,6 +2872,7 @@ HKCU,Software\Wine\AppDefaults\GW2.Main_Win64_Retail.exe\DllOverrides,"atiadlxx" HKCU,Software\Wine\AppDefaults\Spider-Man.exe\DllOverrides,"atiadlxx",,"builtin" HKLM,Software\Wow6432Node\lucasarts entertainment company llc\Star Wars: Episode I Racer\v1.0,"Display Height",0x10001,480 HKLM,Software\Wow6432Node\lucasarts entertainment company llc\Star Wars: Episode I Racer\v1.0,"Display Width",0x10001,640 +HKCU,Software\Wine\AppDefaults\RiftApart.exe\DllOverrides,"atiadlxx",,"builtin" ;;App-specific overrides to limit the number of resolutions HKCU,Software\Wine\AppDefaults\DarkSoulsIII.exe\X11 Driver,"LimitNumberOfResolutions",0x2,"32" HKCU,Software\Wine\AppDefaults\sekiro.exe\X11 Driver,"LimitNumberOfResolutions",0x2,"32" From 30989e779df6ba40581d4154b31c3f8792be052b Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 14 Aug 2023 16:21:07 -0300 Subject: [PATCH 728/758] evr: Release sample queue when streaming ends. (cherry picked from commit 072fe5d62a63b9b46de030a5d3d437cf9ff481a8) --- dlls/evr/presenter.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 06592f8766e..97080717288 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -467,6 +467,18 @@ static BOOL video_presenter_sample_queue_pop(struct video_presenter *presenter, return *sample != NULL; } + +static void video_presenter_sample_queue_free(struct video_presenter *presenter) +{ + struct sample_queue *queue = &presenter->thread.queue; + IMFSample *sample; + + while (video_presenter_sample_queue_pop(presenter, &sample)) + IMFSample_Release(sample); + + free(queue->samples); +} + static HRESULT video_presenter_get_sample_surface(IMFSample *sample, IDirect3DSurface9 **surface) { IMFMediaBuffer *buffer; @@ -754,6 +766,7 @@ static HRESULT video_presenter_end_streaming(struct video_presenter *presenter) if (presenter->thread.queue.last_presented) IMFSample_Release(presenter->thread.queue.last_presented); + video_presenter_sample_queue_free(presenter); memset(&presenter->thread, 0, sizeof(presenter->thread)); video_presenter_set_allocator_callback(presenter, NULL); From c9d172d7ecfc2f4c1b8fc4b46edee4e93d470e51 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 31 Jul 2023 11:44:35 -0300 Subject: [PATCH 729/758] evr: Create critical section for sample queue. (cherry picked from commit 7c0731e1849ea2c007783145d7335f7b4fce7081) --- dlls/evr/presenter.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 97080717288..645a2461e4c 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -66,6 +66,7 @@ struct sample_queue unsigned int front; unsigned int back; IMFSample *last_presented; + CRITICAL_SECTION cs; }; struct streaming_thread @@ -425,6 +426,7 @@ static HRESULT video_presenter_sample_queue_init(struct video_presenter *present queue->size = presenter->allocator_capacity; queue->back = queue->size - 1; + InitializeCriticalSection(&queue->cs); return S_OK; } @@ -435,7 +437,7 @@ static void video_presenter_sample_queue_push(struct video_presenter *presenter, struct sample_queue *queue = &presenter->thread.queue; unsigned int idx; - EnterCriticalSection(&presenter->cs); + EnterCriticalSection(&queue->cs); if (queue->used != queue->size) { if (at_front) @@ -446,14 +448,14 @@ static void video_presenter_sample_queue_push(struct video_presenter *presenter, queue->used++; IMFSample_AddRef(sample); } - LeaveCriticalSection(&presenter->cs); + LeaveCriticalSection(&queue->cs); } static BOOL video_presenter_sample_queue_pop(struct video_presenter *presenter, IMFSample **sample) { struct sample_queue *queue = &presenter->thread.queue; - EnterCriticalSection(&presenter->cs); + EnterCriticalSection(&queue->cs); if (queue->used) { *sample = queue->samples[queue->front]; @@ -462,7 +464,7 @@ static BOOL video_presenter_sample_queue_pop(struct video_presenter *presenter, } else *sample = NULL; - LeaveCriticalSection(&presenter->cs); + LeaveCriticalSection(&queue->cs); return *sample != NULL; } @@ -477,6 +479,7 @@ static void video_presenter_sample_queue_free(struct video_presenter *presenter) IMFSample_Release(sample); free(queue->samples); + DeleteCriticalSection(&queue->cs); } static HRESULT video_presenter_get_sample_surface(IMFSample *sample, IDirect3DSurface9 **surface) @@ -502,6 +505,7 @@ static void video_presenter_sample_present(struct video_presenter *presenter, IM { IDirect3DSurface9 *surface, *backbuffer; IDirect3DDevice9 *device; + struct sample_queue *queue = &presenter->thread.queue; HRESULT hr; if (FAILED(hr = video_presenter_get_sample_surface(sample, &surface))) @@ -527,12 +531,12 @@ static void video_presenter_sample_present(struct video_presenter *presenter, IM WARN("Failed to get a backbuffer, hr %#lx.\n", hr); } - EnterCriticalSection(&presenter->cs); - if (presenter->thread.queue.last_presented) - IMFSample_Release(presenter->thread.queue.last_presented); - presenter->thread.queue.last_presented = sample; - IMFSample_AddRef(presenter->thread.queue.last_presented); - LeaveCriticalSection(&presenter->cs); + EnterCriticalSection(&queue->cs); + if (queue->last_presented) + IMFSample_Release(queue->last_presented); + queue->last_presented = sample; + IMFSample_AddRef(queue->last_presented); + LeaveCriticalSection(&queue->cs); IDirect3DSurface9_Release(surface); } From b74885e16ad3cece9fac0bf8910ed4828eb162d0 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Wed, 9 Aug 2023 19:43:42 -0300 Subject: [PATCH 730/758] evr: Don't lock presenter allocator when calling NotifyRelease. The changes in the video sample allocator are not part of the public api specification, it's only made for internal use in the evr presenter implementation. (cherry picked from commit fa3ebd5044d26e6364e0ea0068cb3c4716b50b4e) --- dlls/evr/evr_private.h | 2 ++ dlls/evr/presenter.c | 2 +- dlls/evr/sample.c | 30 ++++++++++++++++++++++++++---- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/dlls/evr/evr_private.h b/dlls/evr/evr_private.h index ef38b0f70cf..93047b50c94 100644 --- a/dlls/evr/evr_private.h +++ b/dlls/evr/evr_private.h @@ -55,4 +55,6 @@ HRESULT evr_filter_create(IUnknown *outer_unk, void **ppv) DECLSPEC_HIDDEN; HRESULT evr_mixer_create(IUnknown *outer_unk, void **ppv) DECLSPEC_HIDDEN; HRESULT evr_presenter_create(IUnknown *outer_unk, void **ppv) DECLSPEC_HIDDEN; +HRESULT create_video_sample_allocator(BOOL lock_notify_release, REFIID riid, void **obj); + #endif /* __EVR_PRIVATE_INCLUDED__ */ diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 645a2461e4c..3ae9b24ad4a 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -2149,7 +2149,7 @@ static HRESULT video_presenter_init_d3d(struct video_presenter *presenter) if (FAILED(hr)) WARN("Failed to set new device for the manager, hr %#lx.\n", hr); - if (SUCCEEDED(hr = MFCreateVideoSampleAllocator(&IID_IMFVideoSampleAllocator, (void **)&presenter->allocator))) + if (SUCCEEDED(hr = create_video_sample_allocator(FALSE, &IID_IMFVideoSampleAllocator, (void **)&presenter->allocator))) { hr = IMFVideoSampleAllocator_SetDirectXManager(presenter->allocator, (IUnknown *)presenter->device_manager); } diff --git a/dlls/evr/sample.c b/dlls/evr/sample.c index 6a1bbf564f5..aa3f120b115 100644 --- a/dlls/evr/sample.c +++ b/dlls/evr/sample.c @@ -395,6 +395,7 @@ struct sample_allocator unsigned int free_sample_count; struct list free_samples; struct list used_samples; + BOOL lock_notify_release; CRITICAL_SECTION cs; }; @@ -809,6 +810,7 @@ static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback struct queued_sample *iter; IUnknown *object = NULL; IMFSample *sample = NULL; + IMFVideoSampleAllocatorNotify *callback = NULL; HRESULT hr; if (FAILED(IMFAsyncResult_GetObject(result, &object))) @@ -836,10 +838,24 @@ static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback IMFSample_Release(sample); if (allocator->callback) - IMFVideoSampleAllocatorNotify_NotifyRelease(allocator->callback); + { + if (allocator->lock_notify_release) + IMFVideoSampleAllocatorNotify_NotifyRelease(allocator->callback); + else + { + callback = allocator->callback; + IMFVideoSampleAllocatorNotify_AddRef(callback); + } + } LeaveCriticalSection(&allocator->cs); + if (callback) + { + IMFVideoSampleAllocatorNotify_NotifyRelease(callback); + IMFVideoSampleAllocatorNotify_Release(callback); + } + return S_OK; } @@ -852,13 +868,11 @@ static const IMFAsyncCallbackVtbl sample_allocator_tracking_callback_vtbl = sample_allocator_tracking_callback_Invoke, }; -HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj) +HRESULT create_video_sample_allocator(BOOL lock_notify_release, REFIID riid, void **obj) { struct sample_allocator *object; HRESULT hr; - TRACE("%s, %p.\n", debugstr_guid(riid), obj); - if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; @@ -868,6 +882,7 @@ HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj) object->refcount = 1; list_init(&object->used_samples); list_init(&object->free_samples); + object->lock_notify_release = lock_notify_release; InitializeCriticalSection(&object->cs); hr = IMFVideoSampleAllocator_QueryInterface(&object->IMFVideoSampleAllocator_iface, riid, obj); @@ -876,6 +891,13 @@ HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj) return hr; } +HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj) +{ + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + return create_video_sample_allocator(TRUE, riid, obj); +} + static HRESULT WINAPI video_sample_QueryInterface(IMFSample *iface, REFIID riid, void **out) { struct video_sample *sample = impl_from_IMFSample(iface); From b655c32a1a1c8d06b4391c34ee8b363e61e47f59 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Thu, 3 Aug 2023 12:23:17 -0300 Subject: [PATCH 731/758] evr: Remove process input handling from streaming thread. (cherry picked from commit f8cf88dcf4e23e23b087e95eda746348aff24ad5) --- dlls/evr/presenter.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 3ae9b24ad4a..2dc01608caf 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -55,7 +55,6 @@ enum streaming_thread_message { EVRM_STOP = WM_USER, EVRM_PRESENT = WM_USER + 1, - EVRM_PROCESS_INPUT = WM_USER + 2, }; struct sample_queue @@ -706,11 +705,6 @@ static DWORD CALLBACK video_presenter_streaming_thread(void *arg) } break; - case EVRM_PROCESS_INPUT: - EnterCriticalSection(&presenter->cs); - video_presenter_process_input(presenter); - LeaveCriticalSection(&presenter->cs); - break; default: ; } @@ -1811,9 +1805,9 @@ static HRESULT WINAPI video_presenter_allocator_cb_NotifyRelease(IMFVideoSampleA { struct video_presenter *presenter = impl_from_IMFVideoSampleAllocatorNotify(iface); - /* Release notification is executed under allocator lock, instead of processing samples here - notify streaming thread. */ - PostThreadMessageW(presenter->thread.tid, EVRM_PROCESS_INPUT, 0, 0); + EnterCriticalSection(&presenter->cs); + video_presenter_process_input(presenter); + LeaveCriticalSection(&presenter->cs); return S_OK; } From abfec0d202512a5c3db0bee75e8860e0e8122057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 8 Aug 2023 17:46:43 +0200 Subject: [PATCH 732/758] Revert "mfmediaengine: Add support for inserting video effects." This reverts commit 5bb1f1fa49afb44a9f4cba896265fe521b939b45. --- dlls/mfmediaengine/main.c | 51 +++------------------------------------ 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 837aaaae106..12263941583 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -150,8 +150,6 @@ struct media_engine { IUnknown *audio; BOOL audio_optional; - IUnknown *video; - BOOL video_optional; } effects; struct { @@ -1047,27 +1045,6 @@ static HRESULT media_engine_create_audio_effect(struct media_engine *engine, IMF return hr; } -static HRESULT media_engine_create_video_effect(struct media_engine *engine, IMFTopologyNode **node) -{ - HRESULT hr; - - *node = NULL; - - if (!engine->effects.video) - return S_OK; - - if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, node))) - return hr; - - IMFTopologyNode_SetObject(*node, (IUnknown *)engine->effects.video); - IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); - - IUnknown_Release(engine->effects.video); - engine->effects.video = NULL; - - return hr; -} - static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1239,7 +1216,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (SUCCEEDED(hr = MFCreateTopology(&topology))) { IMFTopologyNode *sar_node = NULL, *audio_src = NULL, *audio_effect = NULL; - IMFTopologyNode *grabber_node = NULL, *video_src = NULL, *video_effect = NULL; + IMFTopologyNode *grabber_node = NULL, *video_src = NULL; if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) IMFTopology_SetUINT32(topology, &MF_LOW_LATENCY, TRUE); @@ -1286,18 +1263,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (FAILED(hr = media_engine_create_video_renderer(engine, &grabber_node))) WARN("Failed to create video grabber node, hr %#lx.\n", hr); - if (FAILED(media_engine_create_video_effect(engine, &video_effect))) - WARN("Failed to create video effect node, hr %#lx.\n", hr); - - if (grabber_node && video_src && video_effect) - { - IMFTopology_AddNode(topology, video_src); - IMFTopology_AddNode(topology, grabber_node); - IMFTopology_AddNode(topology, video_effect); - IMFTopologyNode_ConnectOutput(video_src, 0, video_effect, 0); - IMFTopologyNode_ConnectOutput(video_effect, 0, grabber_node, 0); - } - else if (grabber_node && video_src) + if (grabber_node && video_src) { IMFTopology_AddNode(topology, video_src); IMFTopology_AddNode(topology, grabber_node); @@ -2653,20 +2619,9 @@ static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *pr static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { - struct media_engine *impl = impl_from_IMFMediaEngineEx(iface); FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); - if (!effect) - return E_POINTER; - - if (impl->effects.video) - return MF_E_INVALIDREQUEST; - - impl->effects.video = effect; - IUnknown_AddRef(impl->effects.video); - impl->effects.video_optional = is_optional; - - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) From 0502211c7f5e096767ca6052454218987c284615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 8 Aug 2023 17:46:54 +0200 Subject: [PATCH 733/758] Revert "mfmediaengine: Add support for inserting audio effects." This reverts commit ee95a94b993550e12befe8b84b2c171a22441de3. --- dlls/mfmediaengine/main.c | 57 +++------------------------------------ 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 12263941583..c7c74119fa3 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -16,7 +16,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "winerror.h" #define COBJMACROS #include @@ -147,11 +146,6 @@ struct media_engine IMFPresentationDescriptor *pd; } presentation; struct - { - IUnknown *audio; - BOOL audio_optional; - } effects; - struct { LONGLONG pts; SIZE size; @@ -1024,27 +1018,6 @@ static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresen return S_OK; } -static HRESULT media_engine_create_audio_effect(struct media_engine *engine, IMFTopologyNode **node) -{ - HRESULT hr; - - *node = NULL; - - if (!engine->effects.audio) - return S_OK; - - if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, node))) - return hr; - - IMFTopologyNode_SetObject(*node, (IUnknown *)engine->effects.audio); - IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); - - IUnknown_Release(engine->effects.audio); - engine->effects.audio = NULL; - - return hr; -} - static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1215,7 +1188,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (SUCCEEDED(hr = MFCreateTopology(&topology))) { - IMFTopologyNode *sar_node = NULL, *audio_src = NULL, *audio_effect = NULL; + IMFTopologyNode *sar_node = NULL, *audio_src = NULL; IMFTopologyNode *grabber_node = NULL, *video_src = NULL; if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) @@ -1229,18 +1202,7 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (FAILED(hr = media_engine_create_audio_renderer(engine, &sar_node))) WARN("Failed to create audio renderer node, hr %#lx.\n", hr); - if (FAILED(media_engine_create_audio_effect(engine, &audio_effect))) - WARN("Failed to create audio effect node, hr %#lx.\n", hr); - - if (sar_node && audio_src && audio_effect) - { - IMFTopology_AddNode(topology, audio_src); - IMFTopology_AddNode(topology, sar_node); - IMFTopology_AddNode(topology, audio_effect); - IMFTopologyNode_ConnectOutput(audio_src, 0, audio_effect, 0); - IMFTopologyNode_ConnectOutput(audio_effect, 0, sar_node, 0); - } - else if (sar_node && audio_src) + if (sar_node && audio_src) { IMFTopology_AddNode(topology, audio_src); IMFTopology_AddNode(topology, sar_node); @@ -1249,8 +1211,6 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (sar_node) IMFTopologyNode_Release(sar_node); - if (audio_effect) - IMFTopologyNode_Release(audio_effect); if (audio_src) IMFTopologyNode_Release(audio_src); } @@ -2626,20 +2586,9 @@ static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IU static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { - struct media_engine *impl = impl_from_IMFMediaEngineEx(iface); FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); - if (!effect) - return E_POINTER; - - if (impl->effects.audio) - return MF_E_INVALIDREQUEST; - - impl->effects.audio = effect; - IUnknown_AddRef(impl->effects.audio); - impl->effects.audio_optional = is_optional; - - return S_OK; + return E_NOTIMPL; } static HRESULT WINAPI media_engine_RemoveAllEffects(IMFMediaEngineEx *iface) From 48cc0087aea36ccb2cc4a8746e93e4131640f664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 5 Dec 2022 23:46:20 +0100 Subject: [PATCH 734/758] mfmediaengine: Add support for inserting video effects. (cherry picked from commit de1dd6cbf79d451ca35cd63810557e3d347e0211) --- dlls/mfmediaengine/main.c | 110 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 3 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index c7c74119fa3..57ddbcc7de0 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -113,6 +113,19 @@ struct rect float left, top, right, bottom; }; +struct effect +{ + IUnknown *object; + BOOL optional; +}; + +struct effects +{ + struct effect *effects; + size_t count; + size_t capacity; +}; + struct media_engine { IMFMediaEngineEx IMFMediaEngineEx_iface; @@ -145,6 +158,7 @@ struct media_engine IMFMediaSource *source; IMFPresentationDescriptor *pd; } presentation; + struct effects video_effects; struct { LONGLONG pts; @@ -1018,6 +1032,46 @@ static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresen return S_OK; } +static HRESULT media_engine_create_effects(struct effect *effects, size_t count, + IMFTopologyNode *src, IMFTopologyNode *sink, IMFTopology *topology) +{ + IMFTopologyNode *last = src; + HRESULT hr = S_OK; + size_t i; + + IMFTopologyNode_AddRef(last); + + for (i = 0; i < count; ++i) + { + IMFTopologyNode *node = NULL; + + if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &node))) + { + WARN("Failed to create transform node, hr %#lx", hr); + break; + } + + IMFTopologyNode_SetObject(node, (IUnknown *)effects[i].object); + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); + + if (effects[i].optional) + IMFTopologyNode_SetUINT32(node, &MF_TOPONODE_CONNECT_METHOD, MF_CONNECT_AS_OPTIONAL); + + IMFTopology_AddNode(topology, node); + IMFTopologyNode_ConnectOutput(last, 0, node, 0); + + IMFTopologyNode_Release(last); + last = node; + } + + IMFTopologyNode_Release(last); + + if (SUCCEEDED(hr)) + hr = IMFTopologyNode_ConnectOutput(last, 0, sink, 0); + + return hr; +} + static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node) { unsigned int category, role; @@ -1105,6 +1159,20 @@ static void media_engine_clear_presentation(struct media_engine *engine) memset(&engine->presentation, 0, sizeof(engine->presentation)); } +static void media_engine_clear_effects(struct effects *effects) +{ + size_t i; + + for (i = 0; i < effects->count; ++i) + { + if (effects->effects[i].object) + IUnknown_Release(effects->effects[i].object); + } + + free(effects->effects); + memset(effects, 0, sizeof(*effects)); +} + static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMediaSource *source) { IMFStreamDescriptor *sd_audio = NULL, *sd_video = NULL; @@ -1227,7 +1295,10 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi { IMFTopology_AddNode(topology, video_src); IMFTopology_AddNode(topology, grabber_node); - IMFTopologyNode_ConnectOutput(video_src, 0, grabber_node, 0); + + if (FAILED(hr = media_engine_create_effects(engine->video_effects.effects, engine->video_effects.count, + video_src, grabber_node, topology))) + WARN("Failed to create video effect nodes, hr %#lx.\n", hr); } if (SUCCEEDED(hr)) @@ -1382,6 +1453,7 @@ static void free_media_engine(struct media_engine *engine) IMFAttributes_Release(engine->attributes); if (engine->resolver) IMFSourceResolver_Release(engine->resolver); + media_engine_clear_effects(&engine->video_effects); media_engine_release_video_frame_resources(engine); media_engine_clear_presentation(engine); if (engine->device_manager) @@ -2577,11 +2649,43 @@ static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *pr return E_NOTIMPL; } +static HRESULT media_engine_insert_effect(struct media_engine *engine, struct effects *effects, IUnknown *object, BOOL is_optional) +{ + HRESULT hr = S_OK; + + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!mf_array_reserve((void **)&effects->effects, &effects->capacity, effects->count + 1, sizeof(*effects->effects))) + { + hr = E_OUTOFMEMORY; + } + else + { + effects->effects[effects->count].object = object; + if (object) + { + IUnknown_AddRef(effects->effects[effects->count].object); + } + effects->effects[effects->count].optional = is_optional; + + effects->count++; + } + + return hr; +} + static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { - FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); + struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("%p, %p, %d.\n", iface, effect, is_optional); + + EnterCriticalSection(&engine->cs); + hr = media_engine_insert_effect(engine, &engine->video_effects, effect, is_optional); + LeaveCriticalSection(&engine->cs); + + return hr; } static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) From d48f4b7aee8e6d5d80ff05de4add36164427d37c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Sun, 4 Dec 2022 00:03:50 +0100 Subject: [PATCH 735/758] mfmediaengine: Add support for inserting audio effects. (cherry picked from commit 91a84cff9570d97be3ba3ce915b0cb91541cb480) --- dlls/mfmediaengine/main.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 57ddbcc7de0..9ad6174baa1 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -159,6 +159,7 @@ struct media_engine IMFPresentationDescriptor *pd; } presentation; struct effects video_effects; + struct effects audio_effects; struct { LONGLONG pts; @@ -1274,7 +1275,10 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi { IMFTopology_AddNode(topology, audio_src); IMFTopology_AddNode(topology, sar_node); - IMFTopologyNode_ConnectOutput(audio_src, 0, sar_node, 0); + + if (FAILED(hr = media_engine_create_effects(engine->audio_effects.effects, engine->audio_effects.count, + audio_src, sar_node, topology))) + WARN("Failed to create audio effect nodes, hr %#lx.\n", hr); } if (sar_node) @@ -1453,6 +1457,7 @@ static void free_media_engine(struct media_engine *engine) IMFAttributes_Release(engine->attributes); if (engine->resolver) IMFSourceResolver_Release(engine->resolver); + media_engine_clear_effects(&engine->audio_effects); media_engine_clear_effects(&engine->video_effects); media_engine_release_video_frame_resources(engine); media_engine_clear_presentation(engine); @@ -2690,9 +2695,16 @@ static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IU static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) { - FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); + struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("%p, %p, %d.\n", iface, effect, is_optional); + + EnterCriticalSection(&engine->cs); + hr = media_engine_insert_effect(engine, &engine->audio_effects, effect, is_optional); + LeaveCriticalSection(&engine->cs); + + return hr; } static HRESULT WINAPI media_engine_RemoveAllEffects(IMFMediaEngineEx *iface) From 247739b7ee227d1e53bb97adf2dc1795a2b68abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 18 Jul 2023 13:55:08 +0200 Subject: [PATCH 736/758] mfmediaengine: Implement RemoveAllEffects(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 13106b2b38ca01178060be15c96ee5382cb625dd) --- dlls/mfmediaengine/main.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 9ad6174baa1..bbe7248c145 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -2709,9 +2709,22 @@ static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IU static HRESULT WINAPI media_engine_RemoveAllEffects(IMFMediaEngineEx *iface) { - FIXME("%p stub.\n", iface); + struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("%p.\n", iface); + + EnterCriticalSection(&engine->cs); + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + { + media_engine_clear_effects(&engine->audio_effects); + media_engine_clear_effects(&engine->video_effects); + } + LeaveCriticalSection(&engine->cs); + + return hr; } static HRESULT WINAPI media_engine_SetTimelineMarkerTimer(IMFMediaEngineEx *iface, double timeout) From a23d8015e3c137514d0244bbebc03b60c8fd651c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 17 Aug 2023 11:21:56 +0200 Subject: [PATCH 737/758] winegstreamer: Use max decoder thread count instead of live latency. CW-Bug-Id: #22581 --- dlls/winegstreamer/wg_transform.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 1369b5f6591..58f6146e45b 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -101,26 +101,6 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst return GST_FLOW_OK; } -static gboolean transform_src_query_latency(struct wg_transform *transform, GstQuery *query) -{ - GST_LOG("transform %p, query %p", transform, query); - gst_query_set_latency(query, transform->attrs.low_latency, 0, 0); - return true; -} - -static gboolean transform_src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) -{ - struct wg_transform *transform = gst_pad_get_element_private(pad); - - switch (query->type) - { - case GST_QUERY_LATENCY: - return transform_src_query_latency(transform, query); - default: - return gst_pad_query_default(pad, parent, query); - } -} - static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_transform *transform = gst_pad_get_element_private(pad); @@ -332,7 +312,6 @@ NTSTATUS wg_transform_create(void *args) goto out; gst_pad_set_element_private(transform->my_src, transform); - gst_pad_set_query_function(transform->my_src, transform_src_query_cb); if (!(transform->output_caps = wg_format_to_caps(&output_format))) goto out; @@ -367,6 +346,16 @@ NTSTATUS wg_transform_create(void *args) gst_caps_unref(raw_caps); goto out; } + + /* When MF_LOW_LATENCY is requested, allow only one additional decoding thread. Native + * usually does hardware decoding, which isn't stricly synchronous, and low-latency + * probably means one frame latency instead. With only one thread, or using live latency + * reply, we force the decoder to work completely synchronously, which is blocking and + * too slow compared to some applications expectations. + */ + if (transform->attrs.low_latency) + gst_util_set_object_arg(G_OBJECT(element), "max-threads", "2"); + break; case WG_MAJOR_TYPE_AUDIO: From e35b85dff6367d439e7c3c84615ca9166f1d7452 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 21 Jul 2023 13:19:31 +0200 Subject: [PATCH 738/758] winevulkan: Update to VK spec version 1.3.258. This does not enable VK_NV_device_generated_commands_compute because the extension will likely see an API breaking naming fix. (cherry picked from commit eb5be00eb005a30df1b7f828269e3c8f002e1e82) --- dlls/winevulkan/make_vulkan | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index ae00d208318..459a4f14d87 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.254" +VK_XML_VERSION = "1.3.258" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -119,6 +119,9 @@ UNSUPPORTED_EXTENSIONS = [ # Deprecated extensions "VK_NV_external_memory_capabilities", "VK_NV_external_memory_win32", + + # Likely broken: https://github.com/KhronosGroup/Vulkan-Docs/issues/2171 + "VK_NV_device_generated_commands_compute", ] # Either internal extensions which aren't present on the win32 platform which From d1bfe0e7a71843fd4956551c4f9550c966083aba Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Sat, 22 Jul 2023 16:46:06 +0200 Subject: [PATCH 739/758] winevulkan: Update to VK spec version 1.3.259. (cherry picked from commit b5e19a33c9360784961918a364175ab2ad93870d) --- dlls/winevulkan/make_vulkan | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 459a4f14d87..4b6a65b6f32 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.258" +VK_XML_VERSION = "1.3.259" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -119,9 +119,6 @@ UNSUPPORTED_EXTENSIONS = [ # Deprecated extensions "VK_NV_external_memory_capabilities", "VK_NV_external_memory_win32", - - # Likely broken: https://github.com/KhronosGroup/Vulkan-Docs/issues/2171 - "VK_NV_device_generated_commands_compute", ] # Either internal extensions which aren't present on the win32 platform which From b9b156a44befd137239b6ac1dffd88033ad3d5df Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 28 Jul 2023 14:22:36 +0200 Subject: [PATCH 740/758] winevulkan: Update to VK spec version 1.3.260. (cherry picked from commit 08499d17e1be37bafe8a3bdea9971998217efa86) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 4b6a65b6f32..73681dcaff1 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -65,7 +65,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.259" +VK_XML_VERSION = "1.3.260" WINE_VK_VERSION = (1, 3) # Filenames to create. From 1a4ab4be72991bbc3308ef06620ad7c75e347361 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 17 Aug 2023 17:57:29 +0300 Subject: [PATCH 741/758] winevulkan: Update vk.xml to 1.3.260. --- dlls/winevulkan/vk.xml | 914 +++++++++++++++++++++++++++++++++++------ 1 file changed, 786 insertions(+), 128 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index 0c4bb61609f..898bd9f967e 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -175,7 +175,7 @@ branch of the member gitlab server. #define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 254 +#define VK_HEADER_VERSION 260 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // Version of this file @@ -390,6 +390,8 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkBuildMicromapFlagsEXT; typedef VkFlags VkMicromapCreateFlagsEXT; typedef VkFlags VkDirectDriverLoadingFlagsLUNARG; + typedef VkFlags64 VkPipelineCreateFlags2KHR; + typedef VkFlags64 VkBufferUsageFlags2KHR; WSI extensions typedef VkFlags VkCompositeAlphaFlagsKHR; @@ -465,6 +467,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkSubmitFlags; typedef VkFlags VkImageFormatConstraintsFlagsFUCHSIA; + typedef VkFlags VkHostImageCopyFlagsEXT; typedef VkFlags VkImageConstraintsInfoFlagsFUCHSIA; typedef VkFlags VkGraphicsPipelineLibraryFlagsEXT; typedef VkFlags VkImageCompressionFlagsEXT; @@ -711,8 +714,6 @@ typedef void* MTLSharedEvent_id; - - @@ -751,6 +752,7 @@ typedef void* MTLSharedEvent_id; + @@ -776,9 +778,15 @@ typedef void* MTLSharedEvent_id; + + + + + + WSI extensions @@ -1179,6 +1187,11 @@ typedef void* MTLSharedEvent_id; uint32_t dstArrayElementArray element within the destination binding to copy to uint32_t descriptorCountNumber of descriptors to write (determines the size of the array pointed by pDescriptors) + + VkStructureType sType + const void* pNext + VkBufferUsageFlags2KHR usage + VkStructureType sType const void* pNext @@ -1439,6 +1452,18 @@ typedef void* MTLSharedEvent_id; VkPipeline basePipelineHandleIf VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of int32_t basePipelineIndexIf VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of + + VkStructureType sType + const void* pNext + VkDeviceAddress deviceAddress + VkDeviceSize size + VkDeviceAddress pipelineDeviceAddressCaptureReplay + + + VkStructureType sType + const void* pNext + VkPipelineCreateFlags2KHR flags + uint32_t bindingVertex buffer binding id uint32_t strideDistance between vertices in bytes (0 = no advancement) @@ -2325,6 +2350,13 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 deviceGeneratedCommands + + VkStructureType sType + void* pNext + VkBool32 deviceGeneratedCompute + VkBool32 deviceGeneratedComputePipelines + VkBool32 deviceGeneratedComputeCaptureReplay + VkStructureType sType const void* pNext @@ -2445,10 +2477,19 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkPipelineBindPoint pipelineBindPoint - VkPipeline pipeline + VkPipeline pipeline VkIndirectCommandsLayoutNV indirectCommandsLayout uint32_t maxSequencesCount + + VkStructureType sType + const void* pNext + VkPipelineBindPoint pipelineBindPoint + VkPipeline pipeline + + + VkDeviceAddress pipelineAddress + VkStructureType sType void* pNext @@ -3638,6 +3679,30 @@ typedef void* MTLSharedEvent_id; VkDeviceSize maxBufferSize + + VkStructureType sType + void* pNext + VkBool32 maintenance5 + + + VkStructureType sType + void* pNext + VkBool32 earlyFragmentMultisampleCoverageAfterSampleCounting + VkBool32 earlyFragmentSampleMaskTestBeforeSampleCounting + VkBool32 depthStencilSwizzleOneSupport + VkBool32 polygonModePointSize + VkBool32 nonStrictSinglePixelWideLinesUseParallelogram + VkBool32 nonStrictWideLinesUseParallelogram + + + VkStructureType sType + const void* pNext + uint32_t viewMask + uint32_t colorAttachmentCount + const VkFormat* pColorAttachmentFormats + VkFormat depthAttachmentFormat + VkFormat stencilAttachmentFormat + VkStructureType sType void* pNext @@ -3816,7 +3881,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext VkExternalMemoryHandleTypeFlagBits handleType - void* pHostPointer + void* pHostPointer VkStructureType sType @@ -4695,7 +4760,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext uint64_t drmFormatModifier - uint32_t drmFormatModifierPlaneCount + uint32_t drmFormatModifierPlaneCount const VkSubresourceLayout* pPlaneLayouts @@ -4945,7 +5010,7 @@ typedef void* MTLSharedEvent_id; uint64_t duration - + VkStructureType sType const void* pNext VkPipelineCreationFeedback* pPipelineCreationFeedbackOutput pipeline creation feedback. @@ -5529,7 +5594,7 @@ typedef void* MTLSharedEvent_id; VkBool32 uniformTexelBufferOffsetSingleTexelAlignment VkDeviceSize maxBufferSize - + VkStructureType sType const void* pNext VkPipelineCompilerControlFlagsAMD compilerControlFlags @@ -5599,6 +5664,10 @@ typedef void* MTLSharedEvent_id; VkDeviceAddress deviceAddress const void* hostAddress + + VkDeviceAddress deviceAddress + const void* hostAddress + VkStructureType sType const void* pNext @@ -6290,6 +6359,89 @@ typedef void* MTLSharedEvent_id; VkBool32 synchronization2 + + VkStructureType sType + void* pNext + VkBool32 hostImageCopy + + + VkStructureType sType + void* pNext + uint32_t copySrcLayoutCount + VkImageLayout* pCopySrcLayouts + uint32_t copyDstLayoutCount + VkImageLayout* pCopyDstLayouts + uint8_t optimalTilingLayoutUUID[VK_UUID_SIZE] + VkBool32 identicalMemoryTypeRequirements + + + VkStructureType sType + const void* pNext + const void* pHostPointer + uint32_t memoryRowLengthSpecified in texels + uint32_t memoryImageHeight + VkImageSubresourceLayers imageSubresource + VkOffset3D imageOffset + VkExtent3D imageExtent + + + VkStructureType sType + const void* pNext + void* pHostPointer + uint32_t memoryRowLengthSpecified in texels + uint32_t memoryImageHeight + VkImageSubresourceLayers imageSubresource + VkOffset3D imageOffset + VkExtent3D imageExtent + + + VkStructureType sType + const void* pNext + VkHostImageCopyFlagsEXT flags + VkImage dstImage + VkImageLayout dstImageLayout + uint32_t regionCount + const VkMemoryToImageCopyEXT* pRegions + + + VkStructureType sType + const void* pNext + VkHostImageCopyFlagsEXT flags + VkImage srcImage + VkImageLayout srcImageLayout + uint32_t regionCount + const VkImageToMemoryCopyEXT* pRegions + + + VkStructureType sType + const void* pNext + VkHostImageCopyFlagsEXT flags + VkImage srcImage + VkImageLayout srcImageLayout + VkImage dstImage + VkImageLayout dstImageLayout + uint32_t regionCount + const VkImageCopy2* pRegions + + + VkStructureType sType + const void* pNext + VkImage image + VkImageLayout oldLayout + VkImageLayout newLayout + VkImageSubresourceRange subresourceRange + + + VkStructureType sType + void* pNext + VkDeviceSize sizeSpecified in bytes + + + VkStructureType sType + void* pNext + VkBool32 optimalDeviceAccessSpecifies if device access is optimal + VkBool32 identicalMemoryLayoutSpecifies if memory layout is identical + VkStructureType sType void* pNext @@ -6366,8 +6518,8 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkDeviceSize commandPoolReservedSize - uint32_t commandPoolMaxCommandBuffers + VkDeviceSize commandPoolReservedSize + uint32_t commandPoolMaxCommandBuffers VkStructureType sType @@ -6698,7 +6850,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkVideoCodingControlFlagsKHR flags + VkVideoCodingControlFlagsKHR flags VkStructureType sType @@ -7594,7 +7746,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext + const void* pNext VkGraphicsPipelineLibraryFlagsEXT flags @@ -7648,7 +7800,7 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 imageCompressionControl - + VkStructureType sType void* pNext VkImageCompressionFlagsEXT imageCompressionFlags @@ -7659,16 +7811,18 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 imageCompressionControlSwapchain - - VkStructureType sType + + VkStructureType sType void* pNext VkImageSubresource imageSubresource - - VkStructureType sType + + + VkStructureType sType void* pNext VkSubresourceLayout subresourceLayout + VkStructureType sType const void* pNext @@ -8250,6 +8404,12 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 rayTracingPositionFetch + + VkStructureType sType + const void* pNext + const VkImageCreateInfo* pCreateInfo + const VkImageSubresource2KHR* pSubresource + VkStructureType sType void* pNext @@ -8362,6 +8522,77 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 screenBufferImport + + VkStructureType sType + void* pNext + VkBool32 cooperativeMatrix + VkBool32 cooperativeMatrixRobustBufferAccess + + + VkStructureType sType + void* pNext + uint32_t MSize + uint32_t NSize + uint32_t KSize + VkComponentTypeKHR AType + VkComponentTypeKHR BType + VkComponentTypeKHR CType + VkComponentTypeKHR ResultType + VkBool32 saturatingAccumulation + VkScopeKHR scope + + + VkStructureType sType + void* pNext + VkShaderStageFlags cooperativeMatrixSupportedStages + + + VkStructureType sType + void* pNext + uint32_t maxExecutionGraphDepth + uint32_t maxExecutionGraphShaderOutputNodes + uint32_t maxExecutionGraphShaderPayloadSize + uint32_t maxExecutionGraphShaderPayloadCount + uint32_t executionGraphDispatchAddressAlignment + + + VkStructureType sType + void* pNext + VkBool32 shaderEnqueue + + + VkStructureType sType + const void* pNext + VkPipelineCreateFlags flags + uint32_t stageCount + const VkPipelineShaderStageCreateInfo* pStages + const VkPipelineLibraryCreateInfoKHR* pLibraryInfo + VkPipelineLayout layout + VkPipeline basePipelineHandle + int32_t basePipelineIndex + + + VkStructureType sType + const void* pNext + const char* pName + uint32_t index + + + VkStructureType sType + void* pNext + VkDeviceSize size + + + uint32_t nodeIndex + uint32_t payloadCount + VkDeviceOrHostAddressConstAMDX payloads + uint64_t payloadStride + + + uint32_t count + VkDeviceOrHostAddressConstAMDX infos + uint64_t stride + @@ -8400,6 +8631,7 @@ typedef void* MTLSharedEvent_id; + @@ -9007,6 +9239,17 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + @@ -9043,11 +9286,16 @@ typedef void* MTLSharedEvent_id; - + + + + + + @@ -9662,25 +9910,6 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - - - - - - - @@ -10136,6 +10365,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -10416,6 +10648,25 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + @@ -11036,6 +11287,12 @@ typedef void* MTLSharedEvent_id; VkRenderPass renderPass VkExtent2D* pGranularity + + void vkGetRenderingAreaGranularityKHR + VkDevice device + const VkRenderingAreaInfoKHR* pRenderingAreaInfo + VkExtent2D* pGranularity + VkResult vkCreateCommandPool VkDevice device @@ -11265,6 +11522,12 @@ typedef void* MTLSharedEvent_id; VkBuffer buffer VkDeviceSize offset + + void vkCmdUpdatePipelineIndirectBufferNV + VkCommandBuffer commandBuffer + VkPipelineBindPoint pipelineBindPoint + VkPipeline pipeline + void vkCmdCopyBuffer VkCommandBuffer commandBuffer @@ -12592,7 +12855,7 @@ typedef void* MTLSharedEvent_id; VkResult vkGetMemoryHostPointerPropertiesEXT VkDevice device VkExternalMemoryHandleTypeFlagBits handleType - const void* pHostPointer + const void* pHostPointer VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties @@ -13331,6 +13594,17 @@ typedef void* MTLSharedEvent_id; VkDevice device VkDeferredOperationKHR operation + + void vkGetPipelineIndirectMemoryRequirementsNV + VkDevice device + const VkComputePipelineCreateInfo* pCreateInfo + VkMemoryRequirements2* pMemoryRequirements + + + VkDeviceAddress vkGetPipelineIndirectDeviceAddressNV + VkDevice device + const VkPipelineIndirectDeviceAddressInfoNV* pInfo + void vkCmdSetCullMode VkCommandBuffer commandBuffer @@ -13363,6 +13637,14 @@ typedef void* MTLSharedEvent_id; const VkRect2D* pScissors + + void vkCmdBindIndexBuffer2KHR + VkCommandBuffer commandBuffer + VkBuffer buffer + VkDeviceSize offset + VkDeviceSize size + VkIndexType indexType + void vkCmdBindVertexBuffers2 VkCommandBuffer commandBuffer @@ -13787,6 +14069,27 @@ typedef void* MTLSharedEvent_id; uint32_t* pCheckpointDataCount VkCheckpointData2NV* pCheckpointData + + VkResult vkCopyMemoryToImageEXT + VkDevice device + const VkCopyMemoryToImageInfoEXT* pCopyMemoryToImageInfo + + + VkResult vkCopyImageToMemoryEXT + VkDevice device + const VkCopyImageToMemoryInfoEXT* pCopyImageToMemoryInfo + + + VkResult vkCopyImageToImageEXT + VkDevice device + const VkCopyImageToImageInfoEXT* pCopyImageToImageInfo + + + VkResult vkTransitionImageLayoutEXT + VkDevice device + uint32_t transitionCount + const VkHostImageLayoutTransitionInfoEXT* pTransitions + void vkGetCommandPoolMemoryConsumption VkDevice device @@ -14194,12 +14497,13 @@ typedef void* MTLSharedEvent_id; VkShaderModuleIdentifierEXT* pIdentifier - void vkGetImageSubresourceLayout2EXT + void vkGetImageSubresourceLayout2KHR VkDevice device VkImage image - const VkImageSubresource2EXT* pSubresource - VkSubresourceLayout2EXT* pLayout + const VkImageSubresource2KHR* pSubresource + VkSubresourceLayout2KHR* pLayout + VkResult vkGetPipelinePropertiesEXT VkDevice device @@ -14274,6 +14578,12 @@ typedef void* MTLSharedEvent_id; VkDevice device const VkReleaseSwapchainImagesInfoEXT* pReleaseInfo + + void vkGetDeviceImageSubresourceLayoutKHR + VkDevice device + const VkDeviceImageSubresourceInfoKHR* pInfo + VkSubresourceLayout2KHR* pLayout + VkResult vkMapMemory2KHR VkDevice device @@ -14319,6 +14629,57 @@ typedef void* MTLSharedEvent_id; const struct _screen_buffer* buffer VkScreenBufferPropertiesQNX* pProperties + + VkResult vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR + VkPhysicalDevice physicalDevice + uint32_t* pPropertyCount + VkCooperativeMatrixPropertiesKHR* pProperties + + + VkResult vkGetExecutionGraphPipelineScratchSizeAMDX + VkDevice device + VkPipeline executionGraph + VkExecutionGraphPipelineScratchSizeAMDX* pSizeInfo + + + VkResult vkGetExecutionGraphPipelineNodeIndexAMDX + VkDevice device + VkPipeline executionGraph + const VkPipelineShaderStageNodeCreateInfoAMDX* pNodeInfo + uint32_t* pNodeIndex + + + VkResult vkCreateExecutionGraphPipelinesAMDX + VkDevice device + VkPipelineCache pipelineCache + uint32_t createInfoCount + const VkExecutionGraphPipelineCreateInfoAMDX* pCreateInfos + const VkAllocationCallbacks* pAllocator + VkPipeline* pPipelines + + + void vkCmdInitializeGraphScratchMemoryAMDX + VkCommandBuffer commandBuffer + VkDeviceAddress scratch + + + void vkCmdDispatchGraphAMDX + VkCommandBuffer commandBuffer + VkDeviceAddress scratch + const VkDispatchGraphCountInfoAMDX* pCountInfo + + + void vkCmdDispatchGraphIndirectAMDX + VkCommandBuffer commandBuffer + VkDeviceAddress scratch + const VkDispatchGraphCountInfoAMDX* pCountInfo + + + void vkCmdDispatchGraphIndirectCountAMDX + VkCommandBuffer commandBuffer + VkDeviceAddress scratch + VkDeviceAddress countInfo + @@ -16260,8 +16621,6 @@ typedef void* MTLSharedEvent_id; - - @@ -16273,6 +16632,10 @@ typedef void* MTLSharedEvent_id; + + + + @@ -17680,11 +18043,36 @@ typedef void* MTLSharedEvent_id; - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -17901,7 +18289,6 @@ typedef void* MTLSharedEvent_id; - @@ -17963,6 +18350,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -18066,7 +18456,6 @@ typedef void* MTLSharedEvent_id; - @@ -18137,6 +18526,7 @@ typedef void* MTLSharedEvent_id; + @@ -18306,7 +18696,6 @@ typedef void* MTLSharedEvent_id; - @@ -18351,7 +18740,6 @@ typedef void* MTLSharedEvent_id; - @@ -18369,6 +18757,12 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -18435,6 +18829,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -19158,7 +19555,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19242,7 +19639,22 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + @@ -19508,12 +19920,42 @@ typedef void* MTLSharedEvent_id; - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -19682,7 +20124,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20508,9 +20950,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -20777,7 +21219,6 @@ typedef void* MTLSharedEvent_id; - @@ -20807,6 +21248,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -20823,13 +21267,14 @@ typedef void* MTLSharedEvent_id; - + - + + @@ -21198,9 +21643,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -21213,6 +21658,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -21469,10 +21917,23 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + + + + + @@ -21604,6 +22065,7 @@ typedef void* MTLSharedEvent_id; + @@ -21650,6 +22112,7 @@ typedef void* MTLSharedEvent_id; + @@ -21693,16 +22156,6 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - @@ -21728,15 +22181,39 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + @@ -21909,7 +22386,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21918,10 +22395,132 @@ typedef void* MTLSharedEvent_id; - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22060,16 +22659,6 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - @@ -22087,6 +22676,30 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + @@ -22104,6 +22717,8 @@ typedef void* MTLSharedEvent_id; + + @@ -22293,10 +22908,19 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + @@ -22307,6 +22931,7 @@ typedef void* MTLSharedEvent_id; + @@ -22412,7 +23037,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22542,6 +23167,18 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + @@ -22578,8 +23215,8 @@ typedef void* MTLSharedEvent_id; - + @@ -22588,6 +23225,15 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + @@ -23136,15 +23782,15 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -23182,7 +23828,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23264,15 +23910,15 @@ typedef void* MTLSharedEvent_id; - + - - + + @@ -24071,21 +24717,21 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -24102,7 +24748,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24115,14 +24761,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -24140,6 +24786,12 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -24274,12 +24926,12 @@ typedef void* MTLSharedEvent_id; - + - + @@ -24626,6 +25278,12 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -24676,7 +25334,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24735,7 +25393,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24765,17 +25423,17 @@ typedef void* MTLSharedEvent_id; - + - + - + - + @@ -24819,13 +25477,13 @@ typedef void* MTLSharedEvent_id; - + - + - + @@ -24861,7 +25519,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24873,13 +25531,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -24934,7 +25592,7 @@ typedef void* MTLSharedEvent_id; VK_PIPELINE_STAGE_2_HOST_BIT - VK_PIPELINE_STAGE_2_SUBPASS_SHADING_BIT_HUAWEI + VK_PIPELINE_STAGE_2_SUBPASS_SHADER_BIT_HUAWEI VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV From 2871d2b55acc983b84657cc9563c6c7d67d375c0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 16 Aug 2023 20:22:01 -0600 Subject: [PATCH 742/758] winex11.drv: Release old drawables after setting new ones in sync_context(). (cherry picked from commit a743f085df51120a6baa0543d48ca5d1f915afcc) CW-Bug-Id: #22608 --- dlls/winex11.drv/opengl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index cedc17894d8..93f2b734c7e 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1361,18 +1361,19 @@ static void mark_drawable_dirty( struct gl_drawable *old, struct gl_drawable *ne static inline void sync_context(struct wgl_context *context) { BOOL refresh = FALSE; + struct gl_drawable *old[2] = { NULL }; pthread_mutex_lock( &context_mutex ); if (context->new_drawables[0]) { - release_gl_drawable( context->drawables[0] ); + old[0] = context->drawables[0]; context->drawables[0] = context->new_drawables[0]; context->new_drawables[0] = NULL; refresh = TRUE; } if (context->new_drawables[1]) { - release_gl_drawable( context->drawables[1] ); + old[1] = context->drawables[1]; context->drawables[1] = context->new_drawables[1]; context->new_drawables[1] = NULL; refresh = TRUE; @@ -1384,6 +1385,8 @@ static inline void sync_context(struct wgl_context *context) context->drawables[1]->drawable, context->ctx); else pglXMakeCurrent(gdi_display, context->drawables[0]->drawable, context->ctx); + release_gl_drawable( old[0] ); + release_gl_drawable( old[1] ); } pthread_mutex_unlock( &context_mutex ); } From e46681222c3bd5f21a6e752ad25b827d82921349 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 17 Aug 2023 19:28:17 -0600 Subject: [PATCH 743/758] winex11.drv: fshack: Sync GL drawable outside of win data lock in X11DRV_WindowPosChanged(). CW-Bug-Id: #22608 --- dlls/winex11.drv/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 61548c4d5ca..94f1cb15ceb 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3250,7 +3250,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags, set_hwnd_style_props( data->display, data->whole_window, data->hwnd ); - if (data->fs_hack) sync_gl_drawable( hwnd, FALSE ); + if (data->fs_hack) needs_resize = TRUE; /* check if we are currently processing an event relevant to this window */ event_type = 0; From 06aefdc1e010c6c4ca6d2b515059066c7dcd575e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 17 Aug 2023 17:32:41 -0600 Subject: [PATCH 744/758] fixup! winex11.drv: Sync parent's GL drawable when child window is destroyed. CW-Bug-Id: #22608 --- dlls/winex11.drv/opengl.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 93f2b734c7e..d6188f4d029 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1476,6 +1476,11 @@ static enum dc_gl_layered_type get_gl_layered_type( HWND hwnd ) return ret; } +static BOOL drawable_needs_clipping( HWND hwnd, BOOL known_child ) +{ + if (known_child) return TRUE; + return NtUserGetWindowRelative( hwnd, GW_CHILD ) || NtUserGetAncestor( hwnd, GA_PARENT ) != NtUserGetDesktopWindow(); +} /*********************************************************************** * create_gl_drawable @@ -1520,8 +1525,7 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel } TRACE( "%p created pixmap drawable %lx for layered window, type %u.\n", hwnd, gl->drawable, gl->layered_type ); } - else if (!known_child && !NtUserGetWindowRelative( hwnd, GW_CHILD ) && - NtUserGetAncestor( hwnd, GA_PARENT ) == NtUserGetDesktopWindow()) /* childless top-level window */ + else if (!drawable_needs_clipping( hwnd, known_child )) /* childless top-level window */ { struct x11drv_win_data *data; @@ -1666,6 +1670,9 @@ void sync_gl_drawable( HWND hwnd, BOOL known_child ) if (!(old = get_gl_drawable( hwnd, 0 ))) return; new_layered_type = get_gl_layered_type( hwnd ); + + known_child = drawable_needs_clipping( hwnd, known_child ); + if (old->type == DC_GL_PIXMAP_WIN || (known_child && old->type == DC_GL_WINDOW) || (!known_child && old->type != DC_GL_WINDOW) || old->layered_type != new_layered_type) From 6f61d3f4afaddd721d47a598e7966a78c454d9a7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 17 Aug 2023 19:57:51 -0600 Subject: [PATCH 745/758] fixup! fshack: winex11: Use window dimensions in GL if fshack is enabled for gamma only. CW-Bug-Id: #22608 --- dlls/winex11.drv/opengl.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index d6188f4d029..82b99a3c8e3 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -2999,6 +2999,8 @@ static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer POINT scaled_origin; HMONITOR monitor; struct fs_hack_gl_state state; + struct x11drv_win_data *data; + BOOL window_fs_hack = FALSE; const float *gamma_ramp; LONG gamma_serial; unsigned int i; @@ -3007,7 +3009,13 @@ static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer hwnd = NtUserWindowFromDC( ctx->hdc ); monitor = fs_hack_monitor_from_hwnd( hwnd ); - if (fs_hack_enabled( monitor )) + if ((data = get_win_data( hwnd ))) + { + window_fs_hack = data->fs_hack; + release_win_data( data ); + } + + if (window_fs_hack) { user_rect = fs_hack_current_mode( monitor ); real_rect = fs_hack_real_mode( monitor ); From 889d65fb5402abe1054611ef23d58f57d72a4707 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 17 Aug 2023 19:22:27 -0600 Subject: [PATCH 746/758] winex11.drv: fshack/GL: Support fshack on offscreen drawables. CW-Bug-Id: #22608 --- dlls/winex11.drv/opengl.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 82b99a3c8e3..660ae9f7b1b 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1542,6 +1542,8 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel #ifdef SONAME_LIBXCOMPOSITE else if(usexcomposite) { + struct x11drv_win_data *data; + gl->type = DC_GL_CHILD_WIN; gl->window = create_client_window( hwnd, visual ); if (gl->window) @@ -1549,6 +1551,10 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel gl->drawable = pglXCreateWindow( gdi_display, gl->format->fbconfig, gl->window, NULL ); pXCompositeRedirectWindow( gdi_display, gl->window, CompositeRedirectManual ); } + data = get_win_data( hwnd ); + gl->fs_hack = data->fs_hack || fs_hack_get_gamma_ramp( NULL ); + if (gl->fs_hack) TRACE( "Window %p has the fullscreen hack enabled\n", hwnd ); + release_win_data( data ); TRACE( "%p created child %lx drawable %lx\n", hwnd, gl->window, gl->drawable ); } #endif @@ -3033,11 +3039,20 @@ static void fs_hack_blit_framebuffer( struct gl_drawable *gl, GLenum draw_buffer src.cy = user_rect.bottom - user_rect.top; real.cx = real_rect.right - real_rect.left; real.cy = real_rect.bottom - real_rect.top; - scaled_origin.x = user_rect.left; - scaled_origin.y = user_rect.top; - fs_hack_point_user_to_real( &scaled_origin ); - scaled_origin.x -= real_rect.left; - scaled_origin.y -= real_rect.top; + if (gl->type != DC_GL_CHILD_WIN) + { + scaled_origin.x = user_rect.left; + scaled_origin.y = user_rect.top; + fs_hack_point_user_to_real( &scaled_origin ); + scaled_origin.x -= real_rect.left; + scaled_origin.y -= real_rect.top; + } + else + { + /* ExtEscape performs the fshack offset. */ + scaled_origin.x = 0; + scaled_origin.y = 0; + } gamma_ramp = fs_hack_get_gamma_ramp( &gamma_serial ); @@ -4798,12 +4813,6 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; /* fall through */ default: - if (escape.drawable && pglXSwapBuffersMscOML) - { - pglFlush(); - target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); - break; - } if (gl->fs_hack) { ctx->fs_hack = gl->fs_hack; @@ -4816,6 +4825,12 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) ctx->fs_hack = FALSE; fs_hack_setup_context( ctx, gl ); } + if (escape.drawable && pglXSwapBuffersMscOML) + { + pglFlush(); + target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); + break; + } pglXSwapBuffers(gdi_display, gl->drawable); break; } From 5e7ac119d2def0b89d92f3907310ba270ae4bb32 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 17 Aug 2023 14:36:39 -0600 Subject: [PATCH 747/758] winex11.drv: fshack/GL: Always blit fs_hack in wglFlush and wglFinish when drawing to front buffer. CW-Bug-Id: #22608 --- dlls/winex11.drv/opengl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 660ae9f7b1b..3d21093cad6 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -221,6 +221,7 @@ struct wgl_context GLuint fs_hack_gamma_pgm, ramp_ubo; POINT setup_for; GLuint current_draw_fbo, current_read_fbo; + BOOL drawing_to_front; struct list entry; }; @@ -2710,6 +2711,9 @@ static void wglDrawBuffer( GLenum buffer ) { struct wgl_context *ctx = NtCurrentTeb()->glContext; + TRACE( "buffer %#x.\n", buffer ); + + ctx->drawing_to_front = (buffer == GL_FRONT); if (ctx->fs_hack && ctx->current_draw_fbo == ctx->fs_hack_fbo) { TRACE( "Overriding %#x with GL_COLOR_ATTACHMENT0\n", buffer ); @@ -3324,7 +3328,7 @@ static void wglFinish(void) { ctx->fs_hack = gl->fs_hack; if (!gl->fs_hack_context_set_up) fs_hack_setup_context( ctx, gl ); - if (!gl->fs_hack_did_swapbuf) fs_hack_blit_framebuffer( gl, GL_FRONT ); + if (!gl->fs_hack_did_swapbuf || ctx->drawing_to_front) fs_hack_blit_framebuffer( gl, GL_FRONT ); } else if (gl->fs_hack_context_set_up) { @@ -3365,7 +3369,7 @@ static void wglFlush(void) { ctx->fs_hack = gl->fs_hack; if (!gl->fs_hack_context_set_up) fs_hack_setup_context( ctx, gl ); - if (!gl->fs_hack_did_swapbuf) fs_hack_blit_framebuffer( gl, GL_FRONT ); + if (!gl->fs_hack_did_swapbuf || ctx->drawing_to_front) fs_hack_blit_framebuffer( gl, GL_FRONT ); } else if (gl->fs_hack_context_set_up) { From b7fabc51a4e1de4e44abbab7318bbf5f8133da98 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 18 Aug 2023 16:13:27 -0600 Subject: [PATCH 748/758] wine.inf: Enable builtin amd_ags_x64 for Rainbow Six Extraction. CW-Bug-Id: #22634 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 6273413d92e..6043e8ce784 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2852,6 +2852,7 @@ HKCU,Software\Wine\AppDefaults\u4.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" HKCU,Software\Wine\AppDefaults\tll.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" HKCU,Software\Wine\AppDefaults\SOPFFO.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" HKCU,Software\Wine\AppDefaults\RiftApart.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" +HKCU,Software\Wine\AppDefaults\R6-Extraction.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" ;;App-specific overrides for atiadlxx.dll. HKCU,Software\Wine\AppDefaults\s2_sp64_ship.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\s2_mp64_ship.exe\DllOverrides,"atiadlxx",,"builtin" From 2ff67ca14bf3a5481f0ad870d6b882d6b4443665 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 18 Aug 2023 18:44:58 -0600 Subject: [PATCH 749/758] ntdll: HACK: Enable vk_x11_override_min_image_count, vk_x11_strict_image_count for Rainbow Six Extraction. CW-Bug-Id: #22634 --- dlls/ntdll/unix/loader.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 0395883034a..3e4790249f8 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2400,6 +2400,13 @@ static void hacks_init(void) ERR("HACK: setting WINE_ENABLE_GST_LIVE_LATENCY.\n"); setenv("WINE_ENABLE_GST_LIVE_LATENCY", "1", 0); } + + if (sgi && !strcmp(sgi, "2379390")) + { + ERR("HACK: setting vk_x11_override_min_image_count, vk_x11_strict_image_count.\n"); + setenv("vk_x11_override_min_image_count", "2", 0); + setenv("vk_x11_strict_image_count", "true", 0); + } } #ifdef _WIN64 From a7b7e2af347a73dadfeb8690e573b03ff0c9b918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 21 Aug 2023 10:09:12 +0200 Subject: [PATCH 750/758] amend! winegstreamer: Use max decoder thread count instead of live latency. HACK: winegstreamer: Do not report live latency for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/h264_decoder.c | 6 ++++++ dlls/winegstreamer/wg_transform.c | 31 +++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index c6e817df6b0..bcc40839881 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -113,6 +113,12 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) if (SUCCEEDED(IMFAttributes_GetUINT32(decoder->attributes, &MF_LOW_LATENCY, &low_latency))) attrs.low_latency = !!low_latency; + { + const char *sgi; + if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + attrs.low_latency = FALSE; + } + if (!(decoder->wg_transform = wg_transform_create(&input_format, &output_format, &attrs))) return E_FAIL; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 58f6146e45b..1369b5f6591 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -101,6 +101,26 @@ static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, Gst return GST_FLOW_OK; } +static gboolean transform_src_query_latency(struct wg_transform *transform, GstQuery *query) +{ + GST_LOG("transform %p, query %p", transform, query); + gst_query_set_latency(query, transform->attrs.low_latency, 0, 0); + return true; +} + +static gboolean transform_src_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct wg_transform *transform = gst_pad_get_element_private(pad); + + switch (query->type) + { + case GST_QUERY_LATENCY: + return transform_src_query_latency(transform, query); + default: + return gst_pad_query_default(pad, parent, query); + } +} + static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) { struct wg_transform *transform = gst_pad_get_element_private(pad); @@ -312,6 +332,7 @@ NTSTATUS wg_transform_create(void *args) goto out; gst_pad_set_element_private(transform->my_src, transform); + gst_pad_set_query_function(transform->my_src, transform_src_query_cb); if (!(transform->output_caps = wg_format_to_caps(&output_format))) goto out; @@ -346,16 +367,6 @@ NTSTATUS wg_transform_create(void *args) gst_caps_unref(raw_caps); goto out; } - - /* When MF_LOW_LATENCY is requested, allow only one additional decoding thread. Native - * usually does hardware decoding, which isn't stricly synchronous, and low-latency - * probably means one frame latency instead. With only one thread, or using live latency - * reply, we force the decoder to work completely synchronously, which is blocking and - * too slow compared to some applications expectations. - */ - if (transform->attrs.low_latency) - gst_util_set_object_arg(G_OBJECT(element), "max-threads", "2"); - break; case WG_MAJOR_TYPE_AUDIO: From 8d2ed3d66bb166f5024aacdb454df10e189b75d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 21 Aug 2023 19:17:48 +0200 Subject: [PATCH 751/758] HACK: winegstreamer: Don't add unnecessary and slow? videoflip for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/wg_transform.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 1369b5f6591..a6df4a4d0e0 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -401,6 +401,19 @@ NTSTATUS wg_transform_create(void *args) break; case WG_MAJOR_TYPE_VIDEO: + { + const char *sgi; + if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + { + if (!(element = create_element("videoconvert", "base")) + || !append_element(transform->container, element, &first, &last)) + goto out; + gst_util_set_object_arg(G_OBJECT(element), "n-threads", "0"); + /* HACK: skip slow?? videoflip for some games */ + break; + } + } + if (!(element = create_element("videoconvert", "base")) || !append_element(transform->container, element, &first, &last)) goto out; From ca4e8d678d19a0a41c91851ac316d06b0c3ec246 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Aug 2023 12:52:48 -0600 Subject: [PATCH 752/758] HACK: winegstreamer: Disable MF_SA_D3D11_AWARE for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/h264_decoder.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index bcc40839881..1b1646a9ed3 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -887,6 +887,13 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) goto failed; if (FAILED(hr = IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, TRUE))) goto failed; + + { + const char *sgi; + if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, FALSE); + } + if (FAILED(hr = MFCreateAttributes(&decoder->output_attributes, 0))) goto failed; if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) From 394382c5f7bd09ed60afdff8d9615d1dc612af5a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Aug 2023 20:26:03 -0600 Subject: [PATCH 753/758] kernelbase: HACK: Try harder to force GL QtWebEngine rendering for EADesktop. CW-Bug-Id: #22640 --- dlls/kernelbase/process.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 69230d244f2..46b22897712 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1925,6 +1925,16 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableW( LPCWSTR name, LPCWSTR val len = lstrlenW(names[i]); if (size > len && !memcmp( module + size - len, names[i], len * sizeof(*module) )) { + HMODULE h = GetModuleHandleW(L"Qt5Core.dll"); + void (WINAPI *QCoreApplication_setAttribute)(int attr, BOOL set); + + QCoreApplication_setAttribute = (void *)GetProcAddress(h, "?setAttribute@QCoreApplication@@SAXW4ApplicationAttribute@Qt@@_N@Z"); + if (QCoreApplication_setAttribute) + { + QCoreApplication_setAttribute(16 /* AA_UseOpenGLES */, 0); + QCoreApplication_setAttribute(15 /* AA_UseDesktopOpenGL */, 1); + } + else ERR("QCoreApplication_setAttribute not found, h %p.\n", h); value = L"desktop"; FIXME( "HACK: setting QT_OPENGL=desktop.\n" ); break; From 89e03033874f5dd9bbf6ce80ffee3f457dff8468 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 25 Aug 2023 14:40:35 -0600 Subject: [PATCH 754/758] amd_ags_x64: Support version 5.0.5. Elden Ring is using this version. --- dlls/amd_ags_x64/amd_ags_x64_main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 6a8167d0a59..aaeac0f315f 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -27,6 +27,7 @@ static const char radeon_version[] = "23.8.1"; enum amd_ags_version { + AMD_AGS_VERSION_5_0_5, AMD_AGS_VERSION_5_1_1, AMD_AGS_VERSION_5_2_0, AMD_AGS_VERSION_5_2_1, @@ -51,6 +52,7 @@ static const struct } amd_ags_info[AMD_AGS_VERSION_COUNT] = { + {5, 0, 5, sizeof(AGSDeviceInfo_511), sizeof(AGSDX11ReturnedParams_511)}, {5, 1, 1, sizeof(AGSDeviceInfo_511), sizeof(AGSDX11ReturnedParams_511)}, {5, 2, 0, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520)}, {5, 2, 1, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520)}, @@ -63,22 +65,22 @@ amd_ags_info[AMD_AGS_VERSION_COUNT] = {6, 1, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, }; -#define DEF_FIELD(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ +#define DEF_FIELD(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), offsetof(AGSDeviceInfo_600, name), \ offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} -#define DEF_FIELD_520_BELOW(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ +#define DEF_FIELD_520_BELOW(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_520, name), -1, \ -1, -1, -1, -1, -1}} -#define DEF_FIELD_540_UP(name) {DEVICE_FIELD_##name, {-1, -1, \ +#define DEF_FIELD_540_UP(name) {DEVICE_FIELD_##name, {-1, -1, -1, \ -1, -1, offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), offsetof(AGSDeviceInfo_600, name), \ offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} -#define DEF_FIELD_540_600(name) {DEVICE_FIELD_##name, {-1, -1, \ +#define DEF_FIELD_540_600(name) {DEVICE_FIELD_##name, {-1, -1, -1, \ -1, -1, offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), \ -1, -1, -1}} -#define DEF_FIELD_600_BELOW(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ +#define DEF_FIELD_600_BELOW(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), -1, \ -1, -1}} From dcd2b82f2bade4976c6e1fee40ebb81b305b433b Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 26 Aug 2023 13:52:05 +0100 Subject: [PATCH 755/758] amd_ags_x64: Create DXGI factory for AGS context Signed-off-by: Joshua Ashton --- dlls/amd_ags_x64/amd_ags_x64_main.c | 38 ++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index aaeac0f315f..c0d5f1fc61c 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -11,10 +11,11 @@ #include "wine/asm.h" #define COBJMACROS +#include "initguid.h" + #include "d3d11.h" #include "d3d12.h" - -#include "initguid.h" +#include "dxgi1_6.h" #include "dxvk_interfaces.h" @@ -134,14 +135,16 @@ struct AGSContext struct AGSDeviceInfo *devices; VkPhysicalDeviceProperties *properties; VkPhysicalDeviceMemoryProperties *memory_properties; + IDXGIFactory1 *dxgi_factory; ID3D11DeviceContext *d3d11_context; AGSDX11ExtensionsSupported_600 extensions; }; -static HMODULE hd3d11, hd3d12; +static HMODULE hd3d11, hd3d12, hdxgi; static typeof(D3D12CreateDevice) *pD3D12CreateDevice; static typeof(D3D11CreateDevice) *pD3D11CreateDevice; static typeof(D3D11CreateDeviceAndSwapChain) *pD3D11CreateDeviceAndSwapChain; +static typeof(CreateDXGIFactory1) *pCreateDXGIFactory1; static BOOL load_d3d12_functions(void) { @@ -168,6 +171,18 @@ static BOOL load_d3d11_functions(void) return TRUE; } +static BOOL load_dxgi_functions(void) +{ + if (hdxgi) + return TRUE; + + if (!(hdxgi = LoadLibraryA("dxgi.dll"))) + return FALSE; + + pCreateDXGIFactory1 = (void *)GetProcAddress(hdxgi, "CreateDXGIFactory1"); + return TRUE; +} + static AGSReturnCode vk_get_physical_device_properties(unsigned int *out_count, VkPhysicalDeviceProperties **out, VkPhysicalDeviceMemoryProperties **out_memory) { @@ -480,6 +495,18 @@ static AGSReturnCode init_ags_context(AGSContext *context) memset(context, 0, sizeof(*context)); + if (!load_dxgi_functions()) + { + ERR("Could not load dxgi.dll.\n"); + return AGS_MISSING_D3D_DLL; + } + + if (FAILED(pCreateDXGIFactory1(&IID_IDXGIFactory1, (void**)&context->dxgi_factory))) + { + ERR("Failed to create DXGIFactory1.\n"); + return AGS_DX_FAILURE; + } + context->version = determine_ags_version(); ret = vk_get_physical_device_properties(&context->device_count, &context->properties, &context->memory_properties); @@ -653,6 +680,11 @@ AGSReturnCode WINAPI agsDeInitialize(AGSContext *context) if (!context) return AGS_SUCCESS; + if (context->dxgi_factory) + { + IDXGIFactory1_Release(context->dxgi_factory); + context->dxgi_factory = NULL; + } if (context->d3d11_context) { ID3D11DeviceContext_Release(context->d3d11_context); From 4527715759c9bc4b03ce19f96bd8f9415e3f7dc8 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 26 Aug 2023 13:52:40 +0100 Subject: [PATCH 756/758] amd_ags_x64: Rewrite display code to use IDXGIOutput Also allows us to expose HDR features and colorimetry of the display. Signed-off-by: Joshua Ashton --- dlls/amd_ags_x64/amd_ags_x64_main.c | 213 ++++++++++++++++------------ 1 file changed, 122 insertions(+), 91 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index c0d5f1fc61c..91cf9bcb0fa 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -354,117 +354,148 @@ static enum amd_ags_version determine_ags_version(void) return ret; } -struct monitor_enum_context_600 +static void init_device_displays_600(IDXGIFactory1 *factory, const char *adapter_name, AGSDisplayInfo_600 **ret_displays, int *ret_display_count) { - const char *adapter_name; - AGSDisplayInfo_600 **ret_displays; - int *ret_display_count; -}; + IDXGIAdapter1 *adapter = NULL; + WCHAR wide_adapter_name[128]; + UINT adapter_index = 0; -static BOOL WINAPI monitor_enum_proc_600(HMONITOR hmonitor, HDC hdc, RECT *rect, LPARAM context) -{ - struct monitor_enum_context_600 *c = (struct monitor_enum_context_600 *)context; - MONITORINFOEXA monitor_info; - AGSDisplayInfo_600 *new_alloc; - DISPLAY_DEVICEA device; - AGSDisplayInfo_600 *info; - unsigned int i, mode; - DEVMODEA dev_mode; - - - monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfoA(hmonitor, (MONITORINFO *)&monitor_info); - TRACE("monitor_info.szDevice %s.\n", debugstr_a(monitor_info.szDevice)); - - device.cb = sizeof(device); - i = 0; - while (EnumDisplayDevicesA(NULL, i, &device, 0)) + TRACE("adapter_name %s.\n", debugstr_a(adapter_name)); + + *ret_displays = NULL; + *ret_display_count = 0; + + MultiByteToWideChar(CP_ACP, 0, adapter_name, -1, wide_adapter_name, ARRAY_SIZE(wide_adapter_name)); + + while (SUCCEEDED(IDXGIFactory1_EnumAdapters1(factory, adapter_index++, &adapter))) { - TRACE("device.DeviceName %s, device.DeviceString %s.\n", debugstr_a(device.DeviceName), debugstr_a(device.DeviceString)); - ++i; - if (strcmp(device.DeviceString, c->adapter_name) || strcmp(device.DeviceName, monitor_info.szDevice)) - continue; + DXGI_ADAPTER_DESC1 adapter_desc; + IDXGIOutput *output; + UINT output_index; - if (*c->ret_display_count) - { - if (!(new_alloc = heap_realloc(*c->ret_displays, sizeof(*new_alloc) * (*c->ret_display_count + 1)))) - { - ERR("No memory."); - return FALSE; - } - *c->ret_displays = new_alloc; - } - else if (!(*c->ret_displays = heap_alloc(sizeof(**c->ret_displays)))) - { - ERR("No memory."); - return FALSE; - } - info = &(*c->ret_displays)[*c->ret_display_count]; - memset(info, 0, sizeof(*info)); - strcpy(info->displayDeviceName, device.DeviceName); - if (EnumDisplayDevicesA(info->displayDeviceName, 0, &device, 0)) + IDXGIAdapter1_GetDesc1(adapter, &adapter_desc); + + if (wcscmp(wide_adapter_name, adapter_desc.Description)) { - strcpy(info->name, device.DeviceString); + IDXGIAdapter1_Release(adapter); + continue; } - else + + output_index = 0; + while (SUCCEEDED(IDXGIAdapter1_EnumOutputs(adapter, output_index, &output))) { - ERR("Could not get monitor name for device %s.\n", debugstr_a(info->displayDeviceName)); - strcpy(info->name, "Unknown"); + IDXGIOutput_Release(output); + output_index++; } - if (monitor_info.dwFlags & MONITORINFOF_PRIMARY) - info->isPrimaryDisplay = 1; - mode = 0; - memset(&dev_mode, 0, sizeof(dev_mode)); - dev_mode.dmSize = sizeof(dev_mode); - while (EnumDisplaySettingsExA(monitor_info.szDevice, mode, &dev_mode, EDS_RAWMODE)) + *ret_display_count = output_index; + *ret_displays = heap_alloc(*ret_display_count * sizeof(AGSDisplayInfo_600)); + + output_index = 0; + while (SUCCEEDED(IDXGIAdapter1_EnumOutputs(adapter, output_index++, &output))) { - ++mode; - if (dev_mode.dmPelsWidth > info->maxResolutionX) - info->maxResolutionX = dev_mode.dmPelsWidth; - if (dev_mode.dmPelsHeight > info->maxResolutionY) - info->maxResolutionY = dev_mode.dmPelsHeight; - if (dev_mode.dmDisplayFrequency > info->maxRefreshRate) - info->maxRefreshRate = dev_mode.dmDisplayFrequency; + DXGI_OUTPUT_DESC1 output_desc; + MONITORINFOEXA monitor_info; + AGSDisplayInfo_600 *info; + DISPLAY_DEVICEA device; + IDXGIOutput6 *output6; + DEVMODEA dev_mode; + unsigned int mode; + + info = &(*ret_displays)[output_index - 1]; + memset(info, 0, sizeof(*info)); + + if (FAILED(IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput6, (void**)&output6))) + { + ERR("Failed to query IDXGIOutput6\n"); + IDXGIOutput_Release(output); + break; + } + + IDXGIOutput6_GetDesc1(output6, &output_desc); + + monitor_info.cbSize = sizeof(monitor_info); + GetMonitorInfoA(output_desc.Monitor, (MONITORINFO *)&monitor_info); + TRACE("monitor_info.szDevice %s.\n", debugstr_a(monitor_info.szDevice)); + + if (EnumDisplayDevicesA(monitor_info.szDevice, 0, &device, 0)) + { + strcpy(info->name, device.DeviceString); + } + else + { + ERR("Could not get monitor name for device %s.\n", debugstr_a(info->displayDeviceName)); + strcpy(info->name, "Unknown"); + } + strcpy(info->displayDeviceName, monitor_info.szDevice); + if (monitor_info.dwFlags & MONITORINFOF_PRIMARY) + info->isPrimaryDisplay = 1; + if (output_desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) + { + TRACE("Reporting monitor %s as HDR10 supported.\n", debugstr_a(monitor_info.szDevice)); + info->HDR10 = 1; + } + + mode = 0; memset(&dev_mode, 0, sizeof(dev_mode)); dev_mode.dmSize = sizeof(dev_mode); - } + while (EnumDisplaySettingsExA(monitor_info.szDevice, mode, &dev_mode, EDS_RAWMODE)) + { + ++mode; + if (dev_mode.dmPelsWidth > info->maxResolutionX) + info->maxResolutionX = dev_mode.dmPelsWidth; + if (dev_mode.dmPelsHeight > info->maxResolutionY) + info->maxResolutionY = dev_mode.dmPelsHeight; + if (dev_mode.dmDisplayFrequency > info->maxRefreshRate) + info->maxRefreshRate = dev_mode.dmDisplayFrequency; + memset(&dev_mode, 0, sizeof(dev_mode)); + dev_mode.dmSize = sizeof(dev_mode); + } - info->currentResolution.offsetX = monitor_info.rcMonitor.left; - info->currentResolution.offsetY = monitor_info.rcMonitor.top; - info->currentResolution.width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left; - info->currentResolution.height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top; - info->visibleResolution = info->currentResolution; + info->currentResolution.offsetX = monitor_info.rcMonitor.left; + info->currentResolution.offsetY = monitor_info.rcMonitor.top; + info->currentResolution.width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left; + info->currentResolution.height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top; + info->visibleResolution = info->currentResolution; - memset(&dev_mode, 0, sizeof(dev_mode)); - dev_mode.dmSize = sizeof(dev_mode); + memset(&dev_mode, 0, sizeof(dev_mode)); + dev_mode.dmSize = sizeof(dev_mode); - if (EnumDisplaySettingsExA(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode, EDS_RAWMODE)) - info->currentRefreshRate = dev_mode.dmDisplayFrequency; - else - ERR("Could not get current display settings.\n"); - ++*c->ret_display_count; + if (EnumDisplaySettingsExA(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode, EDS_RAWMODE)) + info->currentRefreshRate = dev_mode.dmDisplayFrequency; - TRACE("Added display %s for %s.\n", debugstr_a(monitor_info.szDevice), debugstr_a(c->adapter_name)); - } + info->eyefinityGridCoordX = -1; + info->eyefinityGridCoordY = -1; - return TRUE; -} + info->chromaticityRedX = output_desc.RedPrimary[0]; + info->chromaticityRedY = output_desc.RedPrimary[1]; + info->chromaticityGreenX = output_desc.GreenPrimary[0]; + info->chromaticityGreenY = output_desc.GreenPrimary[1]; + info->chromaticityBlueX = output_desc.BluePrimary[0]; + info->chromaticityBlueY = output_desc.BluePrimary[1]; + info->chromaticityWhitePointX = output_desc.WhitePoint[0]; + info->chromaticityWhitePointY = output_desc.WhitePoint[1]; -static void init_device_displays_600(const char *adapter_name, AGSDisplayInfo_600 **ret_displays, int *ret_display_count) -{ - struct monitor_enum_context_600 context; + info->screenDiffuseReflectance = 0; + info->screenSpecularReflectance = 0; - TRACE("adapter_name %s.\n", debugstr_a(adapter_name)); + info->minLuminance = output_desc.MinLuminance; + info->maxLuminance = output_desc.MaxLuminance; + info->avgLuminance = output_desc.MaxFullFrameLuminance; - context.adapter_name = adapter_name; - context.ret_displays = ret_displays; - context.ret_display_count = ret_display_count; + info->logicalDisplayIndex = output_index - 1; + info->adlAdapterIndex = adapter_index - 1; - EnumDisplayMonitors(NULL, NULL, monitor_enum_proc_600, (LPARAM)&context); + IDXGIOutput6_Release(output6); + IDXGIOutput_Release(output); + } + + IDXGIAdapter1_Release(adapter); + break; + } } -static void init_device_displays_511(const char *adapter_name, AGSDisplayInfo_511 **ret_displays, int *ret_display_count) +static void init_device_displays_511(IDXGIFactory1 *factory, const char *adapter_name, AGSDisplayInfo_511 **ret_displays, int *ret_display_count) { AGSDisplayInfo_600 *displays = NULL; int display_count = 0; @@ -472,7 +503,7 @@ static void init_device_displays_511(const char *adapter_name, AGSDisplayInfo_51 *ret_displays = NULL; *ret_display_count = 0; - init_device_displays_600(adapter_name, &displays, &display_count); + init_device_displays_600(factory, adapter_name, &displays, &display_count); if ((*ret_displays = heap_alloc(sizeof(**ret_displays) * display_count))) { @@ -575,13 +606,13 @@ static AGSReturnCode init_ags_context(AGSContext *context) if (context->version >= AMD_AGS_VERSION_6_0_0) { - init_device_displays_600(vk_properties->deviceName, + init_device_displays_600(context->dxgi_factory, vk_properties->deviceName, GET_DEVICE_FIELD_ADDR(device, displays, AGSDisplayInfo_600 *, context->version), GET_DEVICE_FIELD_ADDR(device, numDisplays, int, context->version)); } else { - init_device_displays_511(vk_properties->deviceName, + init_device_displays_511(context->dxgi_factory, vk_properties->deviceName, GET_DEVICE_FIELD_ADDR(device, displays, AGSDisplayInfo_511 *, context->version), GET_DEVICE_FIELD_ADDR(device, numDisplays, int, context->version)); } From e92252bfc839db8fb0df0a9de0d1fd3d6bf0ef9e Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 26 Aug 2023 15:01:32 +0100 Subject: [PATCH 757/758] amd_ags_x64: Add definitions for AGSDisplaySettings_506 The enum stuff is slightly different here. Signed-off-by: Joshua Ashton --- dlls/amd_ags_x64/amd_ags.h | 55 ++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags.h b/dlls/amd_ags_x64/amd_ags.h index f0afda73d37..52276cc1935 100644 --- a/dlls/amd_ags_x64/amd_ags.h +++ b/dlls/amd_ags_x64/amd_ags.h @@ -689,22 +689,54 @@ typedef struct AGSGPUInfo_600 } AGSGPUInfo_600; /// The display mode -typedef enum AGSDisplaySettings_Mode +typedef enum AGSDisplaySettings_Mode_506 { - Mode_SDR, ///< SDR mode - Mode_HDR10_PQ, ///< HDR10 PQ encoding, requiring a 1010102 UNORM swapchain and PQ encoding in the output shader. - Mode_HDR10_scRGB, ///< HDR10 scRGB, requiring an FP16 swapchain. Values of 1.0 == 80 nits, 125.0 == 10000 nits. - Mode_FreesyncHDR_scRGB, ///< Freesync HDR scRGB, requiring an FP16 swapchain. A value of 1.0 == 80 nits. - Mode_FreesyncHDR_Gamma22, ///< Freesync HDR Gamma 2.2, requiring a 1010102 UNORM swapchain. The output needs to be encoded to gamma 2.2. - Mode_DolbyVision, ///< Dolby Vision, requiring an 8888 UNORM swapchain + Mode_506_SDR, ///< SDR mode + Mode_506_scRGB, ///< scRGB, requiring an FP16 swapchain. Values of 1.0 == 80 nits, 125.0 == 10000 nits. Uses REC709 primaries. + Mode_506_PQ, ///< PQ encoding, requiring a 1010102 UNORM swapchain and PQ encoding in the output shader. Uses BT2020 primaries. + Mode_506_DolbyVision ///< Dolby Vision, requiring an 8888 UNORM swapchain +} AGSDisplaySettings_Mode_506; - Mode_Count ///< Number of enumerated display modes -} AGSDisplaySettings_Mode; +typedef enum AGSDisplaySettings_Mode_600 +{ + Mode_600_SDR, ///< SDR mode + Mode_600_HDR10_PQ, ///< HDR10 PQ encoding, requiring a 1010102 UNORM swapchain and PQ encoding in the output shader. + Mode_600_HDR10_scRGB, ///< HDR10 scRGB, requiring an FP16 swapchain. Values of 1.0 == 80 nits, 125.0 == 10000 nits. + Mode_600_FreesyncHDR_scRGB, ///< Freesync HDR scRGB, requiring an FP16 swapchain. A value of 1.0 == 80 nits. + Mode_600_FreesyncHDR_Gamma22, ///< Freesync HDR Gamma 2.2, requiring a 1010102 UNORM swapchain. The output needs to be encoded to gamma 2.2. + Mode_600_DolbyVision, ///< Dolby Vision, requiring an 8888 UNORM swapchain + + Mode_600_Count ///< Number of enumerated display modes +} AGSDisplaySettings_Mode_600; + +/// The struct to specify the display settings to the driver. +typedef struct AGSDisplaySettings_506 +{ + AGSDisplaySettings_Mode_506 mode; ///< The display mode to set the display into + + double chromaticityRedX; ///< Red display primary X coord + double chromaticityRedY; ///< Red display primary Y coord + + double chromaticityGreenX; ///< Green display primary X coord + double chromaticityGreenY; ///< Green display primary Y coord + + double chromaticityBlueX; ///< Blue display primary X coord + double chromaticityBlueY; ///< Blue display primary Y coord + + double chromaticityWhitePointX; ///< White point X coord + double chromaticityWhitePointY; ///< White point Y coord + + double minLuminance; ///< The minimum scene luminance in nits + double maxLuminance; ///< The maximum scene luminance in nits + + double maxContentLightLevel; ///< The maximum content light level in nits (MaxCLL) + double maxFrameAverageLightLevel; ///< The maximum frame average light level in nits (MaxFALL) +} AGSDisplaySettings_506; /// The struct to specify the display settings to the driver. typedef struct AGSDisplaySettings_511 { - AGSDisplaySettings_Mode mode; ///< The display mode to set the display into + AGSDisplaySettings_Mode_600 mode; ///< The display mode to set the display into double chromaticityRedX; ///< Red display primary X coord double chromaticityRedY; ///< Red display primary Y coord @@ -731,7 +763,7 @@ typedef struct AGSDisplaySettings_511 /// The struct to specify the display settings to the driver. typedef struct AGSDisplaySettings_600 { - AGSDisplaySettings_Mode mode; ///< The display mode to set the display into + AGSDisplaySettings_Mode_600 mode; ///< The display mode to set the display into double chromaticityRedX; ///< Red display primary X coord double chromaticityRedY; ///< Red display primary Y coord @@ -757,6 +789,7 @@ typedef struct AGSDisplaySettings_600 typedef union AGSDisplaySettings { + AGSDisplaySettings_506 agsDisplaySettings506; AGSDisplaySettings_511 agsDisplaySettings511; AGSDisplaySettings_600 agsDisplaySettings600; } AGSDisplaySettings; From 47e357a9189b378dd341cb52988c59f8fa27d4b9 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 26 Aug 2023 15:02:15 +0100 Subject: [PATCH 758/758] amd_ags_x64: Implement agsSetDisplayMode Gets HDR working with Elden Ring. Signed-off-by: Joshua Ashton --- dlls/amd_ags_x64/amd_ags_x64_main.c | 85 +++++++++++++++++++++++++++- dlls/amd_ags_x64/dxvk_interfaces.idl | 36 ++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 91cf9bcb0fa..22502b74f76 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -136,6 +136,7 @@ struct AGSContext VkPhysicalDeviceProperties *properties; VkPhysicalDeviceMemoryProperties *memory_properties; IDXGIFactory1 *dxgi_factory; + IDXGIVkInteropFactory1 *dxgi_interop; ID3D11DeviceContext *d3d11_context; AGSDX11ExtensionsSupported_600 extensions; }; @@ -538,6 +539,12 @@ static AGSReturnCode init_ags_context(AGSContext *context) return AGS_DX_FAILURE; } + if (FAILED(IDXGIFactory1_QueryInterface(context->dxgi_factory, &IID_IDXGIVkInteropFactory1, (void**)&context->dxgi_interop))) + { + ERR("Failed to get IDXGIVkInteropFactory1.\n"); + return AGS_DX_FAILURE; + } + context->version = determine_ags_version(); ret = vk_get_physical_device_properties(&context->device_count, &context->properties, &context->memory_properties); @@ -711,6 +718,11 @@ AGSReturnCode WINAPI agsDeInitialize(AGSContext *context) if (!context) return AGS_SUCCESS; + if (context->dxgi_interop) + { + IDXGIVkInteropFactory1_Release(context->dxgi_interop); + context->dxgi_interop = NULL; + } if (context->dxgi_factory) { IDXGIFactory1_Release(context->dxgi_factory); @@ -735,14 +747,85 @@ AGSReturnCode WINAPI agsDeInitialize(AGSContext *context) return AGS_SUCCESS; } +static DXGI_COLOR_SPACE_TYPE convert_ags_colorspace_506(AGSDisplaySettings_Mode_506 mode) +{ + switch (mode) + { + default: + ERR("Unknown color space in AGS: %d\n", mode); + /* fallthrough */ + case Mode_506_SDR: + TRACE("Setting Mode_506_SDR!\n"); + return DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + case Mode_506_PQ: + TRACE("Setting Mode_506_PQ!\n"); + return DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + case Mode_506_scRGB: + TRACE("Setting Mode_506_scRGB!\n"); + return DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + } +} + +static DXGI_COLOR_SPACE_TYPE convert_ags_colorspace_600(AGSDisplaySettings_Mode_600 mode) +{ + switch (mode) + { + default: + ERR("Unknown color space in AGS: %d\n", mode); + /* fallthrough */ + case Mode_600_SDR: + TRACE("Setting Mode_600_SDR!\n"); + return DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + case Mode_600_HDR10_PQ: + TRACE("Setting Mode_600_HDR10_PQ!\n"); + return DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + case Mode_600_HDR10_scRGB: + TRACE("Setting Mode_600_HDR10_scRGB!\n"); + return DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + } +} + +static DXGI_HDR_METADATA_HDR10 convert_ags_metadata(const AGSDisplaySettings_600 *settings) +{ + DXGI_HDR_METADATA_HDR10 metadata; + metadata.RedPrimary[0] = settings->chromaticityRedX * 50000; + metadata.RedPrimary[1] = settings->chromaticityRedY * 50000; + metadata.GreenPrimary[0] = settings->chromaticityGreenX * 50000; + metadata.GreenPrimary[1] = settings->chromaticityGreenY * 50000; + metadata.BluePrimary[0] = settings->chromaticityBlueX * 50000; + metadata.BluePrimary[1] = settings->chromaticityBlueY * 50000; + metadata.WhitePoint[0] = settings->chromaticityWhitePointX * 50000; + metadata.WhitePoint[1] = settings->chromaticityWhitePointY * 50000; + metadata.MaxMasteringLuminance = settings->maxLuminance; + metadata.MinMasteringLuminance = settings->minLuminance / 0.0001f; + metadata.MaxContentLightLevel = settings->maxContentLightLevel; + metadata.MaxFrameAverageLightLevel = settings->maxFrameAverageLightLevel; + return metadata; +} + AGSReturnCode WINAPI agsSetDisplayMode(AGSContext *context, int device_index, int display_index, const AGSDisplaySettings *settings) { - FIXME("context %p device_index %d display_index %d settings %p stub!\n", context, device_index, + const AGSDisplaySettings_506 *settings506 = &settings->agsDisplaySettings506; + const AGSDisplaySettings_600 *settings600 = &settings->agsDisplaySettings600; + DXGI_COLOR_SPACE_TYPE colorspace; + DXGI_HDR_METADATA_HDR10 metadata; + + TRACE("context %p device_index %d display_index %d settings %p\n", context, device_index, display_index, settings); if (!context) return AGS_INVALID_ARGS; + colorspace = context->version < AMD_AGS_VERSION_5_1_1 + ? convert_ags_colorspace_506(settings506->mode) + : convert_ags_colorspace_600(settings600->mode); + /* Settings 506, 511 and 600 are identical aside from enum order + use + * of bitfield flags we do not use. */ + metadata = convert_ags_metadata(settings600); + + if (FAILED(IDXGIVkInteropFactory1_SetGlobalHDRState(context->dxgi_interop, colorspace, &metadata))) + return AGS_DX_FAILURE; + return AGS_SUCCESS; } diff --git a/dlls/amd_ags_x64/dxvk_interfaces.idl b/dlls/amd_ags_x64/dxvk_interfaces.idl index c632d926fb2..208dd3e66d1 100644 --- a/dlls/amd_ags_x64/dxvk_interfaces.idl +++ b/dlls/amd_ags_x64/dxvk_interfaces.idl @@ -17,6 +17,11 @@ */ import "d3d11.idl"; +import "dxgi1_6.idl"; + +typedef struct VkInstance_T *VkInstance; +typedef void (__stdcall *PFN_vkVoidFunction)(void); +typedef PFN_vkVoidFunction (__stdcall *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName); typedef enum D3D11_VK_EXTENSION { @@ -114,3 +119,34 @@ interface ID3D11VkExtContext1 : ID3D11VkExtContext [in] const void *params, [in] UINT32 param_size, [in] void * const *read_resources, [in] UINT32 read_resource_count, [in] void* const *write_resources, [in] UINT32 write_resources_count); } + +[ + object, + uuid(4c5e1b0d-b0c8-4131-bfd8-9b2476f7f408), + local, + pointer_default(unique) +] +interface IDXGIVkInteropFactory : IUnknown +{ + void GetVulkanInstance( + [out] VkInstance *pInstance, + [out] PFN_vkGetInstanceProcAddr *ppfnVkGetInstanceProcAddr); +} + +[ + object, + uuid(2a289dbd-2d0a-4a51-89f7-f2adce465cd6), + local, + pointer_default(unique) +] +interface IDXGIVkInteropFactory1 : IDXGIVkInteropFactory +{ + HRESULT GetGlobalHDRState( + [out] DXGI_COLOR_SPACE_TYPE *pOutColorSpace, + [out] DXGI_HDR_METADATA_HDR10 *ppOutMetadata) = 0; + + HRESULT SetGlobalHDRState( + [in] DXGI_COLOR_SPACE_TYPE ColorSpace, + [in] const DXGI_HDR_METADATA_HDR10 *pMetadata) = 0; +} +